mirror of
https://github.com/3b1b/manim.git
synced 2025-08-01 06:22:54 +08:00
Sure, I *could* separate all these updates into separate reasonable commits, but where's the fun in that?
This commit is contained in:
@ -9,6 +9,7 @@ from helpers import *
|
|||||||
from animation import Animation
|
from animation import Animation
|
||||||
from simple_animations import DelayByOrder
|
from simple_animations import DelayByOrder
|
||||||
from mobject import Mobject, Point, VMobject, Group
|
from mobject import Mobject, Point, VMobject, Group
|
||||||
|
from topics.geometry import Dot
|
||||||
|
|
||||||
class Transform(Animation):
|
class Transform(Animation):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
@ -160,6 +161,26 @@ class ShimmerIn(DelayByOrder):
|
|||||||
mobject.sort_points(lambda p : np.dot(p, DOWN+RIGHT))
|
mobject.sort_points(lambda p : np.dot(p, DOWN+RIGHT))
|
||||||
DelayByOrder.__init__(self, FadeIn(mobject, **kwargs))
|
DelayByOrder.__init__(self, FadeIn(mobject, **kwargs))
|
||||||
|
|
||||||
|
class FocusOn(Transform):
|
||||||
|
CONFIG = {
|
||||||
|
"opacity" : 0.2,
|
||||||
|
"color" : GREY,
|
||||||
|
"run_time" : 2,
|
||||||
|
"remover" : True,
|
||||||
|
}
|
||||||
|
def __init__(self, mobject_or_point, **kwargs):
|
||||||
|
digest_config(self, kwargs)
|
||||||
|
big_dot = Dot(
|
||||||
|
radius = SPACE_WIDTH+SPACE_HEIGHT,
|
||||||
|
stroke_width = 0,
|
||||||
|
fill_color = self.color,
|
||||||
|
fill_opacity = 0,
|
||||||
|
)
|
||||||
|
little_dot = Dot(radius = 0)
|
||||||
|
little_dot.set_fill(self.color, opacity = self.opacity)
|
||||||
|
little_dot.move_to(mobject_or_point)
|
||||||
|
|
||||||
|
Transform.__init__(self, big_dot, little_dot, **kwargs)
|
||||||
|
|
||||||
class Rotate(ApplyMethod):
|
class Rotate(ApplyMethod):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
|
1159
eoc/chapter4.py
1159
eoc/chapter4.py
File diff suppressed because it is too large
Load Diff
@ -37,10 +37,7 @@ class GraphScene(Scene):
|
|||||||
def setup_axes(self, animate = False):
|
def setup_axes(self, animate = False):
|
||||||
x_num_range = float(self.x_max - self.x_min)
|
x_num_range = float(self.x_max - self.x_min)
|
||||||
self.space_unit_to_x = self.x_axis_width/x_num_range
|
self.space_unit_to_x = self.x_axis_width/x_num_range
|
||||||
if self.x_labeled_nums is None:
|
self.x_labeled_nums = self.x_labeled_nums or []
|
||||||
self.x_labeled_nums = np.arange(
|
|
||||||
self.x_min, self.x_max, 2*self.x_tick_frequency
|
|
||||||
)
|
|
||||||
x_axis = NumberLine(
|
x_axis = NumberLine(
|
||||||
x_min = self.x_min,
|
x_min = self.x_min,
|
||||||
x_max = self.x_max,
|
x_max = self.x_max,
|
||||||
@ -67,10 +64,7 @@ class GraphScene(Scene):
|
|||||||
|
|
||||||
y_num_range = float(self.y_max - self.y_min)
|
y_num_range = float(self.y_max - self.y_min)
|
||||||
self.space_unit_to_y = self.y_axis_height/y_num_range
|
self.space_unit_to_y = self.y_axis_height/y_num_range
|
||||||
if self.y_labeled_nums is None:
|
self.y_labeled_nums = self.y_labeled_nums or []
|
||||||
self.y_labeled_nums = np.arange(
|
|
||||||
self.y_min, self.y_max, 2*self.y_tick_frequency
|
|
||||||
)
|
|
||||||
y_axis = NumberLine(
|
y_axis = NumberLine(
|
||||||
x_min = self.y_min,
|
x_min = self.y_min,
|
||||||
x_max = self.y_max,
|
x_max = self.y_max,
|
||||||
@ -223,6 +217,20 @@ class GraphScene(Scene):
|
|||||||
**line_kwargs
|
**line_kwargs
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def get_vertical_lines_to_graph(
|
||||||
|
self, graph,
|
||||||
|
x_min = None,
|
||||||
|
x_max = None,
|
||||||
|
num_lines = 20,
|
||||||
|
**kwargs
|
||||||
|
):
|
||||||
|
x_min = x_min or self.x_min
|
||||||
|
x_max = x_max or self.x_max
|
||||||
|
return VGroup(*[
|
||||||
|
self.get_vertical_line_to_graph(x, graph, **kwargs)
|
||||||
|
for x in np.linspace(x_min, x_max, num_lines)
|
||||||
|
])
|
||||||
|
|
||||||
def get_secant_slope_group(
|
def get_secant_slope_group(
|
||||||
self,
|
self,
|
||||||
x, graph,
|
x, graph,
|
||||||
@ -236,6 +244,15 @@ class GraphScene(Scene):
|
|||||||
secant_line_color = None,
|
secant_line_color = None,
|
||||||
secant_line_length = 10,
|
secant_line_length = 10,
|
||||||
):
|
):
|
||||||
|
"""
|
||||||
|
Resulting group is of the form VGroup(
|
||||||
|
dx_line,
|
||||||
|
df_line,
|
||||||
|
dx_label, (if applicable)
|
||||||
|
df_label, (if applicable)
|
||||||
|
secant_line, (if applicable)
|
||||||
|
)
|
||||||
|
"""
|
||||||
kwargs = locals()
|
kwargs = locals()
|
||||||
kwargs.pop("self")
|
kwargs.pop("self")
|
||||||
group = VGroup()
|
group = VGroup()
|
||||||
|
@ -310,7 +310,7 @@ class Mobject(object):
|
|||||||
def scale_to_fit_height(self, height):
|
def scale_to_fit_height(self, height):
|
||||||
return self.stretch_to_fit(height, 1, stretch = False)
|
return self.stretch_to_fit(height, 1, stretch = False)
|
||||||
|
|
||||||
def space_out_submobjects(self, factor = 1.5):
|
def space_out_submobjects(self, factor = 1.5, **kwargs):
|
||||||
self.scale_in_place(factor)
|
self.scale_in_place(factor)
|
||||||
for submob in self.submobjects:
|
for submob in self.submobjects:
|
||||||
submob.scale_in_place(1./factor)
|
submob.scale_in_place(1./factor)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from vectorized_mobject import VMobject
|
from vectorized_mobject import VMobject, VGroup
|
||||||
from svg_mobject import SVGMobject, VMobjectFromSVGPathstring
|
from svg_mobject import SVGMobject, VMobjectFromSVGPathstring
|
||||||
from topics.geometry import BackgroundRectangle
|
from topics.geometry import BackgroundRectangle
|
||||||
from helpers import *
|
from helpers import *
|
||||||
@ -74,8 +74,24 @@ class TexMobject(SVGMobject):
|
|||||||
result = result.replace("\n", " \\\\ \n ")
|
result = result.replace("\n", " \\\\ \n ")
|
||||||
result = " ".join([self.alignment, result])
|
result = " ".join([self.alignment, result])
|
||||||
result = result.strip()
|
result = result.strip()
|
||||||
|
result = self.remove_stray_braces(result)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def remove_stray_braces(self, tex):
|
||||||
|
"""
|
||||||
|
Makes TexMobject resiliant to unmatched { at start
|
||||||
|
"""
|
||||||
|
num_lefts, num_rights = [
|
||||||
|
tex.count(char)
|
||||||
|
for char in "{}"
|
||||||
|
]
|
||||||
|
if tex.startswith("{") and num_lefts > num_rights:
|
||||||
|
return tex[1:]
|
||||||
|
elif tex.endswith("}") and num_rights > num_lefts:
|
||||||
|
return tex[:-1]
|
||||||
|
return tex
|
||||||
|
|
||||||
|
|
||||||
def get_tex_string(self):
|
def get_tex_string(self):
|
||||||
return self.tex_string
|
return self.tex_string
|
||||||
|
|
||||||
@ -86,7 +102,7 @@ class TexMobject(SVGMobject):
|
|||||||
for expr in self.args:
|
for expr in self.args:
|
||||||
model = TexMobject(expr, **self.CONFIG)
|
model = TexMobject(expr, **self.CONFIG)
|
||||||
new_index = curr_index + len(model.submobjects)
|
new_index = curr_index + len(model.submobjects)
|
||||||
new_submobjects.append(VMobject(
|
new_submobjects.append(VGroup(
|
||||||
*self.submobjects[curr_index:new_index]
|
*self.submobjects[curr_index:new_index]
|
||||||
))
|
))
|
||||||
curr_index = new_index
|
curr_index = new_index
|
||||||
@ -129,8 +145,9 @@ class TextMobject(TexMobject):
|
|||||||
class Brace(TexMobject):
|
class Brace(TexMobject):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
"buff" : 0.2,
|
"buff" : 0.2,
|
||||||
"n_quads" : 3,
|
"width_multiplier" : 2,
|
||||||
"tex_string" : "\\underbrace{%s}"%(3*"\\qquad"),
|
"max_num_quads" : 15,
|
||||||
|
"min_num_quads" : 0,
|
||||||
}
|
}
|
||||||
def __init__(self, mobject, direction = DOWN, **kwargs):
|
def __init__(self, mobject, direction = DOWN, **kwargs):
|
||||||
digest_config(self, kwargs, locals())
|
digest_config(self, kwargs, locals())
|
||||||
@ -141,7 +158,11 @@ class Brace(TexMobject):
|
|||||||
target_width = right[0]-left[0]
|
target_width = right[0]-left[0]
|
||||||
|
|
||||||
## Adding int(target_width) qquads gives approximately the right width
|
## Adding int(target_width) qquads gives approximately the right width
|
||||||
tex_string = "\\underbrace{%s}"%(int(target_width)*"\\qquad")
|
num_quads = np.clip(
|
||||||
|
int(self.width_multiplier*target_width),
|
||||||
|
self.min_num_quads, self.max_num_quads
|
||||||
|
)
|
||||||
|
tex_string = "\\underbrace{%s}"%(num_quads*"\\qquad")
|
||||||
TexMobject.__init__(self, tex_string, **kwargs)
|
TexMobject.__init__(self, tex_string, **kwargs)
|
||||||
self.stretch_to_fit_width(target_width)
|
self.stretch_to_fit_width(target_width)
|
||||||
self.shift(left - self.get_corner(UP+LEFT) + self.buff*DOWN)
|
self.shift(left - self.get_corner(UP+LEFT) + self.buff*DOWN)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
from scene import Scene
|
from scene import Scene
|
||||||
|
from animation.transform import FadeIn
|
||||||
from mobject import Mobject
|
from mobject import Mobject
|
||||||
from topics.geometry import Rectangle
|
from topics.geometry import Rectangle
|
||||||
from camera import MovingCamera, Camera
|
from camera import MovingCamera, Camera
|
||||||
@ -18,6 +19,7 @@ class ZoomedScene(Scene):
|
|||||||
"zoomed_canvas_corner" : UP+RIGHT,
|
"zoomed_canvas_corner" : UP+RIGHT,
|
||||||
"zoomed_canvas_corner_buff" : DEFAULT_MOBJECT_TO_EDGE_BUFFER,
|
"zoomed_canvas_corner_buff" : DEFAULT_MOBJECT_TO_EDGE_BUFFER,
|
||||||
"zoomed_camera_background" : None,
|
"zoomed_camera_background" : None,
|
||||||
|
"little_rectangle_start_position" : ORIGIN,
|
||||||
"zoom_factor" : 6,
|
"zoom_factor" : 6,
|
||||||
"square_color" : WHITE,
|
"square_color" : WHITE,
|
||||||
"zoom_activated" : False,
|
"zoom_activated" : False,
|
||||||
@ -28,6 +30,12 @@ class ZoomedScene(Scene):
|
|||||||
self.setup_zoomed_camera()
|
self.setup_zoomed_camera()
|
||||||
self.zoom_activated = True
|
self.zoom_activated = True
|
||||||
|
|
||||||
|
def animate_activate_zooming(self):
|
||||||
|
self.activate_zooming()
|
||||||
|
self.play(*map(FadeIn, [
|
||||||
|
self.little_rectangle, self.big_rectangle
|
||||||
|
]))
|
||||||
|
|
||||||
def disactivate_zooming(self):
|
def disactivate_zooming(self):
|
||||||
self.remove(self.big_rectangle, self.little_rectangle)
|
self.remove(self.big_rectangle, self.little_rectangle)
|
||||||
self.zoom_activated = False
|
self.zoom_activated = False
|
||||||
@ -71,7 +79,9 @@ class ZoomedScene(Scene):
|
|||||||
def setup_zoomed_camera(self):
|
def setup_zoomed_camera(self):
|
||||||
self.little_rectangle = self.big_rectangle.copy()
|
self.little_rectangle = self.big_rectangle.copy()
|
||||||
self.little_rectangle.scale(1./self.zoom_factor)
|
self.little_rectangle.scale(1./self.zoom_factor)
|
||||||
self.little_rectangle.center()
|
self.little_rectangle.move_to(
|
||||||
|
self.little_rectangle_start_position
|
||||||
|
)
|
||||||
self.zoomed_camera = MovingCamera(
|
self.zoomed_camera = MovingCamera(
|
||||||
self.little_rectangle,
|
self.little_rectangle,
|
||||||
pixel_shape = self.zoomed_canvas_pixel_shape,
|
pixel_shape = self.zoomed_canvas_pixel_shape,
|
||||||
|
@ -242,8 +242,8 @@ class PiCreatureBubbleIntroduction(AnimationGroup):
|
|||||||
}
|
}
|
||||||
def __init__(self, pi_creature, *content, **kwargs):
|
def __init__(self, pi_creature, *content, **kwargs):
|
||||||
digest_config(self, kwargs)
|
digest_config(self, kwargs)
|
||||||
if isinstance(content, Mobject):
|
if isinstance(content[0], Mobject):
|
||||||
bubble_content = content
|
bubble_content = content[0]
|
||||||
else:
|
else:
|
||||||
bubble_content = TextMobject(*content)
|
bubble_content = TextMobject(*content)
|
||||||
bubble = pi_creature.get_bubble(
|
bubble = pi_creature.get_bubble(
|
||||||
@ -390,11 +390,13 @@ class PiCreatureScene(Scene):
|
|||||||
first mobject being animated with each .play call
|
first mobject being animated with each .play call
|
||||||
"""
|
"""
|
||||||
animations = Scene.compile_play_args_to_animation_list(self, *args)
|
animations = Scene.compile_play_args_to_animation_list(self, *args)
|
||||||
if len(animations) == 0:
|
non_pi_creature_anims = filter(
|
||||||
return animations
|
lambda anim : anim.mobject not in self.get_pi_creatures(),
|
||||||
first_anim = animations[0]
|
animations
|
||||||
if isinstance(first_anim, Blink):
|
)
|
||||||
|
if len(non_pi_creature_anims) == 0:
|
||||||
return animations
|
return animations
|
||||||
|
first_anim = non_pi_creature_anims[0]
|
||||||
#Look at ending state
|
#Look at ending state
|
||||||
first_anim.update(1)
|
first_anim.update(1)
|
||||||
point_of_interest = first_anim.mobject.get_center()
|
point_of_interest = first_anim.mobject.get_center()
|
||||||
@ -405,10 +407,10 @@ class PiCreatureScene(Scene):
|
|||||||
continue
|
continue
|
||||||
if pi_creature in first_anim.mobject.submobject_family():
|
if pi_creature in first_anim.mobject.submobject_family():
|
||||||
continue
|
continue
|
||||||
anims_with_pi_creature = [
|
anims_with_pi_creature = filter(
|
||||||
anim for anim in animations
|
lambda anim : pi_creature in anim.mobject.submobject_family(),
|
||||||
if pi_creature in anim.mobject.submobject_family()
|
animations
|
||||||
]
|
)
|
||||||
if anims_with_pi_creature:
|
if anims_with_pi_creature:
|
||||||
for anim in anims_with_pi_creature:
|
for anim in anims_with_pi_creature:
|
||||||
if isinstance(anim, Transform):
|
if isinstance(anim, Transform):
|
||||||
|
@ -154,6 +154,7 @@ class DashedLine(Line):
|
|||||||
for p1, p2, include in zip(points, points[1:], includes):
|
for p1, p2, include in zip(points, points[1:], includes):
|
||||||
if include:
|
if include:
|
||||||
self.add(Line(p1, p2, **self.init_kwargs))
|
self.add(Line(p1, p2, **self.init_kwargs))
|
||||||
|
self.put_start_and_end_on(self.start, self.end)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def get_start(self):
|
def get_start(self):
|
||||||
|
@ -16,6 +16,7 @@ class NumberLine(VMobject):
|
|||||||
"tick_frequency" : 1,
|
"tick_frequency" : 1,
|
||||||
"leftmost_tick" : None, #Defaults to ceil(x_min)
|
"leftmost_tick" : None, #Defaults to ceil(x_min)
|
||||||
"numbers_with_elongated_ticks" : [0],
|
"numbers_with_elongated_ticks" : [0],
|
||||||
|
"numbers_to_show" : None,
|
||||||
"longer_tick_multiple" : 2,
|
"longer_tick_multiple" : 2,
|
||||||
"number_at_center" : 0,
|
"number_at_center" : 0,
|
||||||
"propogate_style_to_family" : True
|
"propogate_style_to_family" : True
|
||||||
@ -62,11 +63,19 @@ class NumberLine(VMobject):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def point_to_number(self, point):
|
def point_to_number(self, point):
|
||||||
dist_from_left = float(point[0]-self.main_line.get_left()[0])
|
left_point, right_point = self.main_line.get_start_and_end()
|
||||||
num_dist_from_left = dist_from_left/self.space_unit_to_num
|
full_vect = right_point-left_point
|
||||||
return self.x_min + num_dist_from_left
|
def distance_from_left(p):
|
||||||
|
return np.dot(p-left_point, full_vect)/np.linalg.norm(full_vect)
|
||||||
|
|
||||||
|
return interpolate(
|
||||||
|
self.x_min, self.x_max,
|
||||||
|
distance_from_left(point)/distance_from_left(right_point)
|
||||||
|
)
|
||||||
|
|
||||||
def default_numbers_to_display(self):
|
def default_numbers_to_display(self):
|
||||||
|
if self.numbers_to_show is not None:
|
||||||
|
return self.numbers_to_show
|
||||||
return np.arange(self.leftmost_tick, self.x_max, 1)
|
return np.arange(self.leftmost_tick, self.x_max, 1)
|
||||||
|
|
||||||
def get_vertical_number_offset(self, direction = DOWN):
|
def get_vertical_number_offset(self, direction = DOWN):
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
|
|
||||||
from helpers import *
|
from helpers import *
|
||||||
|
|
||||||
|
from mobject.vectorized_mobject import VGroup
|
||||||
|
from topics.geometry import Square
|
||||||
from scene import Scene
|
from scene import Scene
|
||||||
from camera import Camera
|
from camera import Camera
|
||||||
|
|
||||||
@ -60,7 +62,24 @@ class ThreeDScene(Scene):
|
|||||||
"camera_class" : ThreeDCamera,
|
"camera_class" : ThreeDCamera,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
##############
|
||||||
|
|
||||||
|
class Cube(VGroup):
|
||||||
|
CONFIG = {
|
||||||
|
"fill_opacity" : 0.75,
|
||||||
|
"fill_color" : BLUE,
|
||||||
|
"stroke_width" : 0,
|
||||||
|
"propogate_style_to_family" : True,
|
||||||
|
"side_length" : 2,
|
||||||
|
}
|
||||||
|
def generate_points(self):
|
||||||
|
faces = [
|
||||||
|
Square(side_length = self.side_length).shift(OUT).apply_function(
|
||||||
|
lambda p : np.dot(p, z_to_vector(vect).T)
|
||||||
|
)
|
||||||
|
for vect in IN, OUT, LEFT, RIGHT, UP, DOWN
|
||||||
|
]
|
||||||
|
self.add(*faces)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user