Files
manim/topics/common_scenes.py

219 lines
6.9 KiB
Python

from constants import *
from scene.scene import Scene
from animation.animation import Animation
from animation.simple_animations import Write, DrawBorderThenFill
from animation.compositions import LaggedStart
from animation.transform import FadeIn, FadeOut, ApplyMethod
from mobject.vectorized_mobject import VGroup
from mobject.tex_mobject import TexMobject, TextMobject
from topics.characters import Mortimer, Randolph, Blink
from topics.objects import PatreonLogo
from topics.geometry import Square, Rectangle, DashedLine
class OpeningQuote(Scene):
CONFIG = {
"quote" : [],
"quote_arg_separator" : " ",
"highlighted_quote_terms" : {},
"author" : "",
"fade_in_kwargs" : {
"submobject_mode" : "lagged_start",
"rate_func" : None,
"lag_factor" : 4,
"run_time" : 5,
},
}
def construct(self):
self.quote = self.get_quote()
self.author = self.get_author(self.quote)
self.play(FadeIn(self.quote, **self.fade_in_kwargs))
self.wait(2)
self.play(Write(self.author, run_time = 3))
self.wait()
def get_quote(self, max_width = FRAME_WIDTH-1):
text_mobject_kwargs = {
"alignment" : "",
"arg_separator" : self.quote_arg_separator,
}
if isinstance(self.quote, str):
quote = TextMobject("``%s''"%self.quote.strip(), **text_mobject_kwargs)
else:
words = ["\\Large ``"] + list(self.quote) + ["''"]
quote = TextMobject(*words, **text_mobject_kwargs)
##TODO, make less hacky
if self.quote_arg_separator == " ":
quote[0].shift(0.2*RIGHT)
quote[-1].shift(0.2*LEFT)
for term, color in self.set_colored_quote_terms.items():
quote.set_color_by_tex(term, color)
quote.to_edge(UP)
if quote.get_width() > max_width:
quote.scale_to_fit_width(max_width)
return quote
def get_author(self, quote):
author = TextMobject("\\Large -" + self.author)
author.next_to(quote, DOWN)
author.set_color(YELLOW)
return author
class PatreonThanks(Scene):
CONFIG = {
"specific_patrons" : [],
"max_patron_group_size" : 20,
"patron_scale_val" : 0.8,
}
def construct(self):
morty = Mortimer()
morty.next_to(ORIGIN, DOWN)
patreon_logo = PatreonLogo()
patreon_logo.to_edge(UP)
n_patrons = len(self.specific_patrons)
patrons = map(TextMobject, self.specific_patrons)
num_groups = float(len(patrons)) / self.max_patron_group_size
proportion_range = np.linspace(0, 1, num_groups + 1)
indices = (len(patrons)*proportion_range).astype('int')
patron_groups = [
VGroup(*patrons[i:j])
for i, j in zip(indices, indices[1:])
]
for i, group in enumerate(patron_groups):
left_group = VGroup(*group[:len(group)/2])
right_group = VGroup(*group[len(group)/2:])
for subgroup, vect in (left_group, LEFT), (right_group, RIGHT):
subgroup.arrange_submobjects(DOWN, aligned_edge = LEFT)
subgroup.scale(self.patron_scale_val)
subgroup.to_edge(vect)
last_group = None
for i, group in enumerate(patron_groups):
anims = []
if last_group is not None:
self.play(
FadeOut(last_group),
morty.look, UP+LEFT
)
else:
anims += [
DrawBorderThenFill(patreon_logo),
]
self.play(
LaggedStart(
FadeIn, group,
run_time = 2,
),
morty.change, "gracious", group.get_corner(UP+LEFT),
*anims
)
self.play(morty.look_at, group.get_corner(DOWN+LEFT))
self.play(morty.look_at, group.get_corner(UP+RIGHT))
self.play(morty.look_at, group.get_corner(DOWN+RIGHT))
self.play(Blink(morty))
last_group = group
class PatreonEndScreen(PatreonThanks):
CONFIG = {
"n_patron_columns" : 3,
"max_patron_width" : 3,
"run_time" : 20,
"randomize_order" : True,
}
def construct(self):
if self.randomize_order:
random.shuffle(self.specific_patrons)
self.add_title()
self.scroll_through_patrons()
def add_title(self):
title = self.title = TextMobject("Clicky Stuffs")
title.scale(1.5)
title.to_edge(UP, buff = MED_SMALL_BUFF)
randy, morty = self.pi_creatures = VGroup(Randolph(), Mortimer())
for pi, vect in (randy, LEFT), (morty, RIGHT):
pi.scale_to_fit_height(title.get_height())
pi.change_mode("thinking")
pi.look(DOWN)
pi.next_to(title, vect, buff = MED_LARGE_BUFF)
self.add_foreground_mobjects(title, randy, morty)
def scroll_through_patrons(self):
logo_box = Square(side_length = 2.5)
logo_box.to_corner(DOWN+LEFT, buff = MED_LARGE_BUFF)
total_width = FRAME_X_RADIUS - logo_box.get_right()[0]
black_rect = Rectangle(
fill_color = BLACK,
fill_opacity = 1,
stroke_width = 0,
width = FRAME_WIDTH,
height = 1.1*FRAME_Y_RADIUS
)
black_rect.to_edge(UP, buff = 0)
line = DashedLine(FRAME_X_RADIUS*LEFT, FRAME_X_RADIUS*RIGHT)
line.move_to(black_rect, DOWN)
line.shift(SMALL_BUFF*SMALL_BUFF*DOWN)
self.add(line)
patrons = VGroup(*map(TextMobject, self.specific_patrons))
patrons.scale(self.patron_scale_val)
for patron in patrons:
if patron.get_width() > self.max_patron_width:
patron.scale_to_fit_width(self.max_patron_width)
columns = VGroup(*[
VGroup(
*patrons[i::self.n_patron_columns]
).arrange_submobjects(DOWN, buff = MED_SMALL_BUFF)
for i in range(self.n_patron_columns)
])
columns.arrange_submobjects(
RIGHT, buff = LARGE_BUFF,
aligned_edge = UP,
)
columns.scale_to_fit_width(total_width - 1)
columns.next_to(black_rect, DOWN, 3*LARGE_BUFF)
columns.to_edge(RIGHT)
self.play(
columns.next_to, FRAME_Y_RADIUS*DOWN, UP, LARGE_BUFF,
columns.to_edge, RIGHT,
Animation(black_rect),
rate_func = None,
run_time = self.run_time,
)
class ExternallyAnimatedScene(Scene):
def construct(self):
raise Exception("Don't actually run this class.")
class TODOStub(Scene):
CONFIG = {
"message" : ""
}
def construct(self):
self.add(TextMobject("TODO: %s"%self.message))
self.wait()