mirror of
https://github.com/3b1b/manim.git
synced 2025-07-31 05:52:34 +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 simple_animations import DelayByOrder
|
||||
from mobject import Mobject, Point, VMobject, Group
|
||||
from topics.geometry import Dot
|
||||
|
||||
class Transform(Animation):
|
||||
CONFIG = {
|
||||
@ -160,6 +161,26 @@ class ShimmerIn(DelayByOrder):
|
||||
mobject.sort_points(lambda p : np.dot(p, DOWN+RIGHT))
|
||||
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):
|
||||
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):
|
||||
x_num_range = float(self.x_max - self.x_min)
|
||||
self.space_unit_to_x = self.x_axis_width/x_num_range
|
||||
if self.x_labeled_nums is None:
|
||||
self.x_labeled_nums = np.arange(
|
||||
self.x_min, self.x_max, 2*self.x_tick_frequency
|
||||
)
|
||||
self.x_labeled_nums = self.x_labeled_nums or []
|
||||
x_axis = NumberLine(
|
||||
x_min = self.x_min,
|
||||
x_max = self.x_max,
|
||||
@ -67,10 +64,7 @@ class GraphScene(Scene):
|
||||
|
||||
y_num_range = float(self.y_max - self.y_min)
|
||||
self.space_unit_to_y = self.y_axis_height/y_num_range
|
||||
if self.y_labeled_nums is None:
|
||||
self.y_labeled_nums = np.arange(
|
||||
self.y_min, self.y_max, 2*self.y_tick_frequency
|
||||
)
|
||||
self.y_labeled_nums = self.y_labeled_nums or []
|
||||
y_axis = NumberLine(
|
||||
x_min = self.y_min,
|
||||
x_max = self.y_max,
|
||||
@ -221,7 +215,21 @@ class GraphScene(Scene):
|
||||
self.coords_to_point(x, 0),
|
||||
self.input_to_graph_point(x, graph),
|
||||
**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(
|
||||
self,
|
||||
@ -236,6 +244,15 @@ class GraphScene(Scene):
|
||||
secant_line_color = None,
|
||||
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.pop("self")
|
||||
group = VGroup()
|
||||
|
@ -310,7 +310,7 @@ class Mobject(object):
|
||||
def scale_to_fit_height(self, height):
|
||||
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)
|
||||
for submob in self.submobjects:
|
||||
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 topics.geometry import BackgroundRectangle
|
||||
from helpers import *
|
||||
@ -74,8 +74,24 @@ class TexMobject(SVGMobject):
|
||||
result = result.replace("\n", " \\\\ \n ")
|
||||
result = " ".join([self.alignment, result])
|
||||
result = result.strip()
|
||||
result = self.remove_stray_braces(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):
|
||||
return self.tex_string
|
||||
|
||||
@ -86,7 +102,7 @@ class TexMobject(SVGMobject):
|
||||
for expr in self.args:
|
||||
model = TexMobject(expr, **self.CONFIG)
|
||||
new_index = curr_index + len(model.submobjects)
|
||||
new_submobjects.append(VMobject(
|
||||
new_submobjects.append(VGroup(
|
||||
*self.submobjects[curr_index:new_index]
|
||||
))
|
||||
curr_index = new_index
|
||||
@ -129,8 +145,9 @@ class TextMobject(TexMobject):
|
||||
class Brace(TexMobject):
|
||||
CONFIG = {
|
||||
"buff" : 0.2,
|
||||
"n_quads" : 3,
|
||||
"tex_string" : "\\underbrace{%s}"%(3*"\\qquad"),
|
||||
"width_multiplier" : 2,
|
||||
"max_num_quads" : 15,
|
||||
"min_num_quads" : 0,
|
||||
}
|
||||
def __init__(self, mobject, direction = DOWN, **kwargs):
|
||||
digest_config(self, kwargs, locals())
|
||||
@ -141,7 +158,11 @@ class Brace(TexMobject):
|
||||
target_width = right[0]-left[0]
|
||||
|
||||
## 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)
|
||||
self.stretch_to_fit_width(target_width)
|
||||
self.shift(left - self.get_corner(UP+LEFT) + self.buff*DOWN)
|
||||
|
@ -1,6 +1,7 @@
|
||||
import numpy as np
|
||||
|
||||
from scene import Scene
|
||||
from animation.transform import FadeIn
|
||||
from mobject import Mobject
|
||||
from topics.geometry import Rectangle
|
||||
from camera import MovingCamera, Camera
|
||||
@ -18,6 +19,7 @@ class ZoomedScene(Scene):
|
||||
"zoomed_canvas_corner" : UP+RIGHT,
|
||||
"zoomed_canvas_corner_buff" : DEFAULT_MOBJECT_TO_EDGE_BUFFER,
|
||||
"zoomed_camera_background" : None,
|
||||
"little_rectangle_start_position" : ORIGIN,
|
||||
"zoom_factor" : 6,
|
||||
"square_color" : WHITE,
|
||||
"zoom_activated" : False,
|
||||
@ -28,6 +30,12 @@ class ZoomedScene(Scene):
|
||||
self.setup_zoomed_camera()
|
||||
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):
|
||||
self.remove(self.big_rectangle, self.little_rectangle)
|
||||
self.zoom_activated = False
|
||||
@ -71,7 +79,9 @@ class ZoomedScene(Scene):
|
||||
def setup_zoomed_camera(self):
|
||||
self.little_rectangle = self.big_rectangle.copy()
|
||||
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.little_rectangle,
|
||||
pixel_shape = self.zoomed_canvas_pixel_shape,
|
||||
|
@ -242,8 +242,8 @@ class PiCreatureBubbleIntroduction(AnimationGroup):
|
||||
}
|
||||
def __init__(self, pi_creature, *content, **kwargs):
|
||||
digest_config(self, kwargs)
|
||||
if isinstance(content, Mobject):
|
||||
bubble_content = content
|
||||
if isinstance(content[0], Mobject):
|
||||
bubble_content = content[0]
|
||||
else:
|
||||
bubble_content = TextMobject(*content)
|
||||
bubble = pi_creature.get_bubble(
|
||||
@ -390,11 +390,13 @@ class PiCreatureScene(Scene):
|
||||
first mobject being animated with each .play call
|
||||
"""
|
||||
animations = Scene.compile_play_args_to_animation_list(self, *args)
|
||||
if len(animations) == 0:
|
||||
return animations
|
||||
first_anim = animations[0]
|
||||
if isinstance(first_anim, Blink):
|
||||
non_pi_creature_anims = filter(
|
||||
lambda anim : anim.mobject not in self.get_pi_creatures(),
|
||||
animations
|
||||
)
|
||||
if len(non_pi_creature_anims) == 0:
|
||||
return animations
|
||||
first_anim = non_pi_creature_anims[0]
|
||||
#Look at ending state
|
||||
first_anim.update(1)
|
||||
point_of_interest = first_anim.mobject.get_center()
|
||||
@ -405,10 +407,10 @@ class PiCreatureScene(Scene):
|
||||
continue
|
||||
if pi_creature in first_anim.mobject.submobject_family():
|
||||
continue
|
||||
anims_with_pi_creature = [
|
||||
anim for anim in animations
|
||||
if pi_creature in anim.mobject.submobject_family()
|
||||
]
|
||||
anims_with_pi_creature = filter(
|
||||
lambda anim : pi_creature in anim.mobject.submobject_family(),
|
||||
animations
|
||||
)
|
||||
if anims_with_pi_creature:
|
||||
for anim in anims_with_pi_creature:
|
||||
if isinstance(anim, Transform):
|
||||
|
@ -154,6 +154,7 @@ class DashedLine(Line):
|
||||
for p1, p2, include in zip(points, points[1:], includes):
|
||||
if include:
|
||||
self.add(Line(p1, p2, **self.init_kwargs))
|
||||
self.put_start_and_end_on(self.start, self.end)
|
||||
return self
|
||||
|
||||
def get_start(self):
|
||||
|
@ -16,6 +16,7 @@ class NumberLine(VMobject):
|
||||
"tick_frequency" : 1,
|
||||
"leftmost_tick" : None, #Defaults to ceil(x_min)
|
||||
"numbers_with_elongated_ticks" : [0],
|
||||
"numbers_to_show" : None,
|
||||
"longer_tick_multiple" : 2,
|
||||
"number_at_center" : 0,
|
||||
"propogate_style_to_family" : True
|
||||
@ -62,11 +63,19 @@ class NumberLine(VMobject):
|
||||
)
|
||||
|
||||
def point_to_number(self, point):
|
||||
dist_from_left = float(point[0]-self.main_line.get_left()[0])
|
||||
num_dist_from_left = dist_from_left/self.space_unit_to_num
|
||||
return self.x_min + num_dist_from_left
|
||||
left_point, right_point = self.main_line.get_start_and_end()
|
||||
full_vect = right_point-left_point
|
||||
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):
|
||||
if self.numbers_to_show is not None:
|
||||
return self.numbers_to_show
|
||||
return np.arange(self.leftmost_tick, self.x_max, 1)
|
||||
|
||||
def get_vertical_number_offset(self, direction = DOWN):
|
||||
|
@ -1,6 +1,8 @@
|
||||
|
||||
from helpers import *
|
||||
|
||||
from mobject.vectorized_mobject import VGroup
|
||||
from topics.geometry import Square
|
||||
from scene import Scene
|
||||
from camera import Camera
|
||||
|
||||
@ -60,7 +62,24 @@ class ThreeDScene(Scene):
|
||||
"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