mirror of
https://github.com/3b1b/manim.git
synced 2025-08-05 16:49:03 +00:00
Merge branch 'master' into eop
This commit is contained in:
commit
08b0cc0dea
13 changed files with 319 additions and 155 deletions
73
active_projects/alt_calc.py
Normal file
73
active_projects/alt_calc.py
Normal 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()
|
||||
|
||||
|
|
@ -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)
|
||||
|
|
|
@ -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):
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
]))
|
||||
|
|
Loading…
Add table
Reference in a new issue