Merge branch 'master' into eop

This commit is contained in:
Ben Hambrecht 2018-05-08 13:18:58 +02:00
commit 08b0cc0dea
13 changed files with 319 additions and 155 deletions

View file

@ -0,0 +1,73 @@
from big_ol_pile_of_manim_imports import *
class NumberlineTransformationScene(Scene):
CONFIG = {
}
def setup(self):
pass
class ExampleNumberlineTransformationScene(NumberlineTransformationScene):
def construct(self):
pass
# Scenes
class WriteOpeningWords(Scene):
def construct(self):
raw_string1 = "Dear calculus student,"
raw_string2 = "You're about to go through your first course. Like " + \
"any new topic, it will take some hard work to understand,"
words1, words2 = [
TextMobject("\\Large", *rs.split(" "))
for rs in raw_string1, raw_string2
]
words1.next_to(words2, UP, aligned_edge=LEFT, buff=LARGE_BUFF)
words = VGroup(*it.chain(words1, words2))
words.scale_to_fit_width(FRAME_WIDTH - 2 * LARGE_BUFF)
words.to_edge(UP)
letter_wait = 0.05
word_wait = 2 * letter_wait
comma_wait = 5 * letter_wait
for word in words:
self.play(LaggedStart(
FadeIn, word,
run_time=len(word) * letter_wait,
lag_ratio=1.5 / len(word)
))
self.wait(word_wait)
if word.get_tex_string()[-1] == ",":
self.wait(comma_wait)
class StartingCalc101(PiCreatureScene):
CONFIG = {
# "default_pi_creature_kwargs": {
# "color": BLUE,
# "flip_at_start": False,
# },
}
def construct(self):
randy = self.pi_creature
deriv_string = "\\frac{df}{dx}(x) = \\lim(\\delta x \\to \\infty)" + \
"{f(x + \\delta x) - f(x) \\over \\delta x}"
equations = VGroup(
TexMobject(*break_up_string_by_terms(deriv_string, "\\delta x"))
)
title = TextMobject("Calculus 101")
title.to_edge(UP)
h_line = Line(LEFT, RIGHT)
h_line.scale_to_fit_width(FRAME_WIDTH - LARGE_BUFF)
h_line.next_to(title, DOWN)
self.add(title, h_line)
self.play(randy.change, "erm", title)
self.wait()

View file

@ -2154,7 +2154,7 @@ class Thumbnail(TransformingAreasYCoord):
words = TextMobject("Cramer's", "rule")
words.scale_to_fit_width(7)
# words.add_background_rectangle_to_parts()
# words.add_background_rectangle_to_submobjects()
words.add_background_rectangle()
words.to_edge(UP)
self.add(words)

View file

@ -58,7 +58,7 @@ class ProbabilityDistributions(PiCreatureScene):
p_rain_whole_label.next_to(brace_rain, UP)
brace_sun = Brace(sun_rect, DOWN)
p_sun_label = TextMobject("$P($sun$)=$").scale(text_scale)
p_sun_label = TextMobject("$P($sunshine$)=$").scale(text_scale)
p_sun_decimal = DecimalNumber(p_sun).scale(text_scale)
p_sun_decimal.next_to(p_sun_label)
p_sun_whole_label = VGroup(p_sun_label, p_sun_decimal)
@ -97,7 +97,7 @@ class ProbabilityDistributions(PiCreatureScene):
new_brace_sun = Brace(new_sun_rect, DOWN)
new_p_sun_label = TextMobject("$P($sun$)=$").scale(text_scale)
new_p_sun_label = TextMobject("$P($sunshine$)=$").scale(text_scale)
new_p_sun_decimal = DecimalNumber(new_p_sun).scale(text_scale)
new_p_sun_decimal.next_to(new_p_sun_label)
new_p_sun_whole_label = VGroup(new_p_sun_label, new_p_sun_decimal)
@ -147,20 +147,28 @@ class ProbabilityDistributions(PiCreatureScene):
self.play(MoveToTarget(forecast))
self.play(
FadeOut(brace_rain),
FadeOut(brace_sun),
FadeOut(p_rain_whole_label),
FadeOut(p_sun_whole_label),
)
# COIN FLIP
coin_flip_rect = BrickRow(3)
coin_flip_rect = BrickRow(3, height = 2, width = 10)
for (i, brick) in enumerate(coin_flip_rect.rects):
tally = TallyStack(3 - i, i)
tally.next_to(brick, UP)
tally.move_to(brick)
coin_flip_rect.add(tally)
coin_flip_rect.scale(0.7)
coin_flip_rect.scale(0.8).shift(2*RIGHT)
self.play(FadeIn(coin_flip_rect))
counts = [1, 3, 3, 1]
@ -180,75 +188,72 @@ class ProbabilityDistributions(PiCreatureScene):
coin_flip_rect.add(braces, labels)
self.play(coin_flip_rect.to_corner,UR)
coin_flip_rect.target = coin_flip_rect.copy().scale(0.6)
coin_flip_rect.target.to_corner(UR, buff = LARGE_BUFF)
self.play(
MoveToTarget(coin_flip_rect)
)
self.play(
FadeOut(braces),
FadeOut(labels)
)
# DOUBLE DICE THROW
cell_size = 0.5
dice_table = TwoDiceTable(cell_size = cell_size, label_scale = 0.7)
dice_table.shift(DOWN)
dice_table.shift(0.8 * DOWN)
dice_unit_rect = SurroundingRectangle(dice_table.cells, buff = 0,
stroke_color = WHITE)
self.play(FadeIn(dice_table))
self.wait()
self.play(
FadeOut(dice_table.rows),
FadeOut(dice_table.labels),
dice_table.cells.fade, 0.8
)
dice_table_braces = VGroup()
dice_table_probs = VGroup()
dice_table_grouped_cells = VGroup()
for i in range(6):
cell = dice_table.cells[6 * i]
start = cell.get_center()
color = cell.get_fill_color()
brace = Brace(cell, LEFT, buff = 0, color = color)
brace.stretch(0.5,0)
stop = start + cell_size * LEFT + cell_size * DOWN
p_label = TexMobject("{" + str(i + 1) + "\over 36}", color = color)
p_label.scale(0.35)
p_label.next_to(brace, LEFT)
dice_table_probs.add(p_label)
dice_table_braces.add(brace)
dice_table_grouped_cells.add(VGroup(*[
dice_table.cells[6 * i - 5 * k]
for k in range(i + 1)
]))
for i in range(5):
cell = dice_table.cells[31 + i]
start = cell.get_center()
color = cell.get_fill_color()
brace = Brace(cell, DOWN, buff = 0, color = color)
brace.stretch(0.5, 1)
stop = start + cell_size * LEFT + cell_size * DOWN
p_label = TexMobject("{" + str(5 - i) + "\over 36}", color = color)
p_label.scale(0.35)
p_label.next_to(brace, DOWN)
dice_table_probs.add(p_label)
dice_table_braces.add(brace)
dice_table_grouped_cells.add(VGroup(*[
dice_table.cells[31 + i - 5 * k]
for k in range(5 - i)
]))
self.play(
FadeIn(dice_unit_rect),
FadeIn(dice_table.rows)
)
# group the dice table cells to make them appear in the right order
for (cell, label) in zip(dice_table.cells, dice_table.labels):
cell.add(label)
self.play(
LaggedStart(ShowCreation, dice_table_braces, lag_ratio = lag_ratio, run_time = run_time),
LaggedStart(Write, dice_table_probs, lag_ratio = lag_ratio, run_time = run_time),
LaggedStart(ApplyMethod, dice_table_grouped_cells, arg_creator =
lambda m : (m.fade, -4), lag_ratio = lag_ratio, run_time = run_time
)
LaggedStart(FadeIn, dice_table_grouped_cells,
lag_ratio = lag_ratio, run_time = run_time)
)
self.wait()
self.play(
FadeOut(dice_table.rows),
FadeOut(dice_unit_rect),
)
self.play(
dice_table_grouped_cells.space_out_submobjects, {"factor" : 1.9},
rate_func=there_and_back_with_pause,
run_time=run_time
)
@ -269,5 +274,6 @@ class ProbabilityDistributions(PiCreatureScene):

View file

@ -6,14 +6,21 @@ from constants import *
from animation.animation import Animation
from animation.movement import Homotopy
from animation.composition import AnimationGroup
from animation.composition import Succession
from animation.creation import ShowCreation
from animation.creation import ShowPartial
from animation.creation import FadeOut
from animation.transform import Transform
from mobject.mobject import Mobject
from mobject.geometry import Circle
from mobject.geometry import Dot
from mobject.shape_matchers import SurroundingRectangle
from utils.bezier import interpolate
from utils.config_ops import digest_config
from utils.rate_functions import squish_rate_func
from utils.rate_functions import there_and_back
from utils.rate_functions import wiggle
class FocusOn(Transform):
@ -93,14 +100,49 @@ class ShowCreationThenDestruction(ShowPassingFlash):
}
class AnimationOnSurroundingRectangle(AnimationGroup):
CONFIG = {
"surrounding_rectangle_config": {},
# Function which takes in a rectangle, and spits
# out some animation. Could be some animation class,
# could be something more
"rect_to_animation": Animation
}
def __init__(self, mobject, **kwargs):
digest_config(self, kwargs)
rect = SurroundingRectangle(
mobject, **self.surrounding_rectangle_config
)
AnimationGroup.__init__(self, self.rect_to_animation(rect, **kwargs))
class ShowPassingFlashAround(AnimationOnSurroundingRectangle):
CONFIG = {
"rect_to_animation": ShowPassingFlash
}
class ShowCreationThenDestructionAround(AnimationOnSurroundingRectangle):
CONFIG = {
"rect_to_animation": ShowCreationThenDestruction
}
class CircleThenFadeAround(AnimationOnSurroundingRectangle):
CONFIG = {
"rect_to_animation": lambda rect: Succession(
ShowCreation, rect,
FadeOut, rect,
)
}
class ApplyWave(Homotopy):
CONFIG = {
"direction": DOWN,
"direction": UP,
"amplitude": 0.2,
"run_time": 1,
"apply_function_kwargs": {
"maintain_smoothness": False,
},
}
def __init__(self, mobject, **kwargs):
@ -110,9 +152,9 @@ class ApplyWave(Homotopy):
vect = self.amplitude * self.direction
def homotopy(x, y, z, t):
start_point = np.array([x, y, z])
alpha = (x - left_x) / (right_x - left_x)
power = np.exp(2 * (alpha - 0.5))
# lf = self.lag_factor
power = np.exp(2.0 * (alpha - 0.5))
nudge = there_and_back(t**power)
return np.array([x, y, z]) + nudge * vect
Homotopy.__init__(self, homotopy, mobject, **kwargs)
@ -195,7 +237,6 @@ class TurnInsideOut(Transform):
}
def __init__(self, mobject, **kwargs):
mobject.sort_points(np.linalg.norm)
mob_copy = mobject.copy()
mob_copy.sort_points(lambda p: -np.linalg.norm(p))
mob_copy.reverse_points()
Transform.__init__(self, mobject, mob_copy, **kwargs)

View file

@ -32,10 +32,10 @@ class PiCreatureScene(Scene):
"seconds_to_blink": 3,
"pi_creatures_start_on_screen": True,
"default_pi_creature_kwargs": {
"color": GREY_BROWN,
"flip_at_start": True,
"color": BLUE,
"flip_at_start": False,
},
"default_pi_creature_start_corner": DOWN + LEFT,
"default_pi_creature_start_corner": DL,
}
def setup(self):

View file

@ -116,10 +116,6 @@ class Matrix(VMobject):
self.brackets = VGroup(l_bracket, r_bracket)
return self
def add_background_rectangle(self):
self.background_rectangle = BackgroundRectangle(self)
self.add_to_back(self.background_rectangle)
def set_color_columns(self, *colors):
for i, color in enumerate(colors):
VGroup(*self.mob_matrix[:, i]).set_color(color)

View file

@ -1,3 +1,5 @@
from __future__ import absolute_import
import copy
import itertools as it
import numpy as np
@ -28,7 +30,6 @@ class Mobject(Container):
"""
CONFIG = {
"color": WHITE,
"stroke_width": DEFAULT_POINT_THICKNESS,
"name": None,
"dim": 3,
"target": None,
@ -464,6 +465,27 @@ class Mobject(Container):
self.shift(start - self.points[0])
return self
# Background rectangle
def add_background_rectangle(self, color=BLACK, opacity=0.75, **kwargs):
from mobject.shape_matchers import BackgroundRectangle
self.background_rectangle = BackgroundRectangle(
self, color=color,
fill_opacity=opacity,
**kwargs
)
self.add_to_back(self.background_rectangle)
return self
def add_background_rectangle_to_submobjects(self, **kwargs):
for submobject in self.submobjects:
submobject.add_background_rectangle(**kwargs)
return self
def add_background_rectangle_to_family_members_with_points(self, **kwargs):
for mob in self.family_members_with_points():
mob.add_background_rectangle(**kwargs)
return self
# Match other mobvject properties
def match_color(self, mobject):
@ -643,6 +665,7 @@ class Mobject(Container):
return result
# Pseudonyms for more general get_critical_point method
def get_edge_center(self, direction):
return self.get_critical_point(direction)
@ -719,7 +742,8 @@ class Mobject(Container):
def submobject_family(self):
sub_families = map(Mobject.submobject_family, self.submobjects)
all_mobjects = [self] + list(it.chain(*sub_families))
# all_mobjects = [self] + list(it.chain(*sub_families))
all_mobjects = list(it.chain(*sub_families)) + [self]
return remove_list_redundancies(all_mobjects)
def family_members_with_points(self):

View file

@ -3,9 +3,7 @@ from __future__ import absolute_import
from constants import *
from mobject.svg.tex_mobject import TexMobject
from mobject.types.vectorized_mobject import VGroup
from mobject.types.vectorized_mobject import VMobject
from mobject.shape_matchers import BackgroundRectangle
class DecimalNumber(VMobject):
@ -51,7 +49,7 @@ class DecimalNumber(VMobject):
)
if self.unit is not None:
self.unit_sign = TexMobject(self.unit, color = self.color)
self.unit_sign = TexMobject(self.unit, color=self.color)
self.add(self.unit_sign)
self.arrange_submobjects(
@ -70,16 +68,6 @@ class DecimalNumber(VMobject):
if self.include_background_rectangle:
self.add_background_rectangle()
def add_background_rectangle(self):
# TODO, is this the best way to handle
# background rectangles?
self.background_rectangle = BackgroundRectangle(self)
self.submobjects = [
self.background_rectangle,
VGroup(*self.submobjects)
]
return self
class Integer(DecimalNumber):
CONFIG = {

View file

@ -2,14 +2,16 @@ from constants import *
from svg_mobject import SVGMobject
from svg_mobject import VMobjectFromSVGPathstring
from mobject.shape_matchers import BackgroundRectangle
from utils.config_ops import digest_config
from utils.strings import split_string_list_to_isolate_substring
from mobject.types.vectorized_mobject import VGroup
from mobject.types.vectorized_mobject import VMobject
from mobject.types.vectorized_mobject import VectorizedPoint
import operator as op
# TODO list
# - Make sure if "color" is passed into TexMobject, it behaves as expected
TEX_MOB_SCALE_FACTOR = 0.05
@ -31,57 +33,36 @@ class TexSymbol(VMobjectFromSVGPathstring):
self.set_fill(opacity=opacity)
class TexMobject(SVGMobject):
class SingleStringTexMobject(SVGMobject):
CONFIG = {
"template_tex_file": TEMPLATE_TEX_FILE,
"stroke_width": 0,
"fill_opacity": 1.0,
"fill_color": WHITE,
"should_center": True,
"arg_separator": " ",
"height": None,
"organize_left_to_right": False,
"propagate_style_to_family": True,
"alignment": "",
}
def __init__(self, *args, **kwargs):
digest_config(self, kwargs, locals())
if "color" in kwargs.keys() and "fill_color" not in kwargs.keys():
self.fill_color = kwargs["color"]
# TODO, Eventually remove this
if len(args) == 1 and isinstance(args[0], list):
self.args = args[0]
##
assert(all([isinstance(a, str) for a in self.args]))
self.tex_string = self.get_modified_expression()
def __init__(self, tex_string, **kwargs):
digest_config(self, kwargs)
assert(isinstance(tex_string, str))
self.tex_string = tex_string
file_name = tex_to_svg_file(
self.tex_string,
self.get_modified_expression(tex_string),
self.template_tex_file
)
SVGMobject.__init__(self, file_name=file_name, **kwargs)
self.scale(TEX_MOB_SCALE_FACTOR)
if self.height is None:
self.scale(TEX_MOB_SCALE_FACTOR)
if self.organize_left_to_right:
self.organize_submobjects_left_to_right()
def path_string_to_mobject(self, path_string):
# Overwrite superclass default to use
# specialized path_string mobject
return TexSymbol(path_string)
def generate_points(self):
SVGMobject.generate_points(self)
if len(self.args) > 1:
self.handle_multiple_args()
def get_modified_expression(self):
result = self.arg_separator.join(self.args)
result = " ".join([self.alignment, result])
def get_modified_expression(self, tex_string):
result = self.alignment + " " + tex_string
result = result.strip()
result = self.modify_special_strings(result)
return result
def modify_special_strings(self, tex):
@ -123,27 +104,67 @@ class TexMobject(SVGMobject):
def get_tex_string(self):
return self.tex_string
def handle_multiple_args(self):
def path_string_to_mobject(self, path_string):
# Overwrite superclass default to use
# specialized path_string mobject
return TexSymbol(path_string)
def organize_submobjects_left_to_right(self):
self.sort_submobjects(lambda p: p[0])
return self
class TexMobject(SingleStringTexMobject):
CONFIG = {
"arg_separator": " ",
"substrings_to_isolate": [],
"tex_to_color_map": {},
}
def __init__(self, *tex_strings, **kwargs):
digest_config(self, kwargs)
tex_strings = self.break_up_tex_strings(tex_strings)
self.tex_strings = tex_strings
SingleStringTexMobject.__init__(
self, self.arg_separator.join(tex_strings), **kwargs
)
self.break_up_by_substrings()
self.set_color_by_tex_to_color_map(self.tex_to_color_map)
if self.organize_left_to_right:
self.organize_submobjects_left_to_right()
def break_up_tex_strings(self, tex_strings):
substrings_to_isolate = op.add(
self.substrings_to_isolate,
self.tex_to_color_map.keys()
)
split_list = split_string_list_to_isolate_substring(
tex_strings, *substrings_to_isolate
)
split_list = map(str.strip, split_list)
split_list = filter(lambda s: s != '', split_list)
return split_list
def break_up_by_substrings(self):
"""
Reorganize existing submojects one layer
deeper based on the structure of args (as a list of strings)
deeper based on the structure of tex_strings (as a list
of tex_strings)
"""
new_submobjects = []
curr_index = 0
self.expression_parts = list(self.args)
for expr in self.args:
sub_tex_mob = TexMobject(expr, **self.CONFIG)
sub_tex_mob.tex_string = expr # Want it unmodified
for tex_string in self.tex_strings:
sub_tex_mob = SingleStringTexMobject(tex_string, **self.CONFIG)
num_submobs = len(sub_tex_mob.submobjects)
new_index = curr_index + num_submobs
if num_submobs == 0:
if len(self) > curr_index:
last_submob_index = curr_index
else:
last_submob_index = -1
sub_tex_mob.submobjects = [VectorizedPoint(
self.submobjects[last_submob_index].get_right()
)]
# For cases like empty tex_strings, we want the corresponing
# part of the whole TexMobject to be a VectorizedPoint
# positioned in the right part of the TexMobject
sub_tex_mob.submobjects = [VectorizedPoint()]
last_submob_index = min(curr_index, len(self.submobjects) - 1)
sub_tex_mob.move_to(self.submobjects[last_submob_index], RIGHT)
else:
sub_tex_mob.submobjects = self.submobjects[curr_index:new_index]
new_submobjects.append(sub_tex_mob)
@ -161,15 +182,9 @@ class TexMobject(SVGMobject):
else:
return tex1 == tex2
tex_submobjects = filter(
lambda m: isinstance(m, TexMobject),
self.submobject_family()
)
if hasattr(self, "expression_parts"):
tex_submobjects.remove(self)
return VGroup(*filter(
lambda m: test(tex, m.get_tex_string()),
tex_submobjects
self.submobjects
))
def get_part_by_tex(self, tex, **kwargs):
@ -185,7 +200,7 @@ class TexMobject(SVGMobject):
def set_color_by_tex_to_color_map(self, texs_to_color_map, **kwargs):
for texs, color in texs_to_color_map.items():
try:
# If the given key behaves like strings
# If the given key behaves like tex_strings
texs + ''
self.set_color_by_tex(texs, color, **kwargs)
except TypeError:
@ -204,32 +219,13 @@ class TexMobject(SVGMobject):
part = self.get_part_by_tex(tex, **kwargs)
return self.index_of_part(part)
def organize_submobjects_left_to_right(self):
self.sort_submobjects(lambda p: p[0])
return self
def sort_submobjects_alphabetically(self):
def alphabetical_cmp(m1, m2):
if not all([isinstance(m, TexMobject) for m in m1, m2]):
return 0
return cmp(m1.get_tex_string(), m2.get_tex_string())
self.submobjects.sort(alphabetical_cmp)
return self
def add_background_rectangle(self, color=BLACK, opacity=0.75, **kwargs):
self.background_rectangle = BackgroundRectangle(
self, color=color,
fill_opacity=opacity,
**kwargs
)
letters = VMobject(*self.submobjects)
self.submobjects = [self.background_rectangle, letters]
return self
def add_background_rectangle_to_parts(self):
for part in self:
part.add_background_rectangle()
return self
def split(self):
# Many old scenes assume that when you pass in a single string
# to TexMobject, it indexes across the characters.
if len(self.submobjects) == 1:
return self.submobjects[0].split()
else:
return super(TexMobject, self).split()
class TextMobject(TexMobject):

View file

@ -12,6 +12,10 @@ from utils.iterables import stretch_array_to_length
class PMobject(Mobject):
CONFIG = {
"stroke_width": DEFAULT_POINT_THICKNESS,
}
def init_points(self):
self.rgbas = np.zeros((0, 4))
self.points = np.zeros((0, 3))

View file

@ -18,6 +18,7 @@ class VMobject(Mobject):
"fill_color": None,
"fill_opacity": 0.0,
"stroke_color": None,
"stroke_width": DEFAULT_POINT_THICKNESS,
# Indicates that it will not be displayed, but
# that it should count in parent mobject's path
"is_subpath": False,

View file

@ -2135,7 +2135,7 @@ class PlugObserverIntoPolynomial(DistanceProductScene):
)
question.scale(text_scale_val)
question.next_to(dot, RIGHT)
question.add_background_rectangle_to_parts()
question.add_background_rectangle_to_submobjects()
f_words = TextMobject("$f$", "of the way")
third_words = TextMobject("$\\frac{1}{3}$", "of the way")

View file

@ -1,5 +1,6 @@
import re
import string
import itertools as it
def to_camel_case(name):
@ -24,3 +25,37 @@ def camel_case_initials(name):
def complex_string(complex_num):
return filter(lambda c: c not in "()", str(complex_num))
def split_string_to_isolate_substrings(full_string, *substrings_to_isolate):
"""
Given a string, and an arbitrary number of possible substrings, returns a list
of strings which would concatenate to make the full string, and in which
these special substrings appear as their own elements.
For example, split_string_to_isolate_substrings("to be or not to be", "to", "be") would
return ["to", " ", "be", " or not ", "to", " ", "be"]
"""
if len(substrings_to_isolate) == 0:
return [full_string]
substring_to_isolate = substrings_to_isolate[0]
all_substrings = list(it.chain(*zip(
full_string.split(substring_to_isolate),
it.repeat(substring_to_isolate)
)))
all_substrings.pop(-1)
all_substrings = filter(lambda s: s != "", all_substrings)
return split_string_list_to_isolate_substring(
all_substrings, *substrings_to_isolate[1:]
)
def split_string_list_to_isolate_substring(string_list, *substrings_to_isolate):
"""
Similar to split_string_to_isolate_substrings, but the first argument
is a list of strings, thought of as something already broken up a bit.
"""
return list(it.chain(*[
split_string_to_isolate_substrings(s, *substrings_to_isolate)
for s in string_list
]))