Sure, I *could* separate all these updates into separate reasonable commits, but where's the fun in that?

This commit is contained in:
Grant Sanderson
2017-02-16 13:03:26 -08:00
parent 0c1bb9d41e
commit 3535b39dd1
10 changed files with 1280 additions and 39 deletions

View File

@ -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 = {

File diff suppressed because it is too large Load Diff

View File

@ -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()

View File

@ -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)

View File

@ -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)

View File

@ -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,

View File

@ -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):

View File

@ -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):

View File

@ -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):

View File

@ -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)