3b1b-manim/topics/common_scenes.py

219 lines
6.8 KiB
Python
Raw Normal View History

2017-03-08 15:18:09 -08:00
from helpers import *
from scene.scene import Scene
2017-12-08 17:57:32 -08:00
from animation import Animation
2018-03-14 14:52:33 -07:00
from animation.simple_animations import Write, DrawBorderThenFill
from animation.compositions import LaggedStart
2017-04-14 23:30:29 -07:00
from animation.transform import FadeIn, FadeOut, ApplyMethod
2017-03-08 15:18:09 -08:00
from mobject.vectorized_mobject import VGroup
from mobject.tex_mobject import TexMobject, TextMobject
2017-12-08 17:57:32 -08:00
from topics.characters import Mortimer, Randolph, Blink
2017-03-08 15:18:09 -08:00
from topics.objects import PatreonLogo
2017-12-08 17:57:32 -08:00
from topics.geometry import Square, Rectangle, DashedLine
2017-03-08 15:18:09 -08:00
class OpeningQuote(Scene):
CONFIG = {
2017-03-29 16:06:10 -07:00
"quote" : [],
2017-03-22 15:06:17 -07:00
"quote_arg_separator" : " ",
2017-03-29 16:06:10 -07:00
"highlighted_quote_terms" : {},
"author" : "",
2017-03-08 15:18:09 -08:00
"fade_in_kwargs" : {
"submobject_mode" : "lagged_start",
"rate_func" : None,
2017-03-22 15:06:17 -07:00
"lag_factor" : 4,
2017-03-08 15:18:09 -08:00
"run_time" : 5,
},
}
def construct(self):
2017-04-12 09:06:04 -07:00
self.quote = self.get_quote()
self.author = self.get_author(self.quote)
2017-03-08 15:18:09 -08:00
2017-04-12 09:06:04 -07:00
self.play(FadeIn(self.quote, **self.fade_in_kwargs))
2018-01-15 19:15:05 -08:00
self.wait(2)
2017-04-12 09:06:04 -07:00
self.play(Write(self.author, run_time = 3))
2018-01-15 19:15:05 -08:00
self.wait()
2017-03-08 15:18:09 -08:00
def get_quote(self, max_width = 2*SPACE_WIDTH-1):
2017-03-22 15:06:17 -07:00
text_mobject_kwargs = {
"alignment" : "",
"arg_separator" : self.quote_arg_separator,
}
2017-03-08 15:18:09 -08:00
if isinstance(self.quote, str):
2017-03-22 15:06:17 -07:00
quote = TextMobject("``%s''"%self.quote.strip(), **text_mobject_kwargs)
2017-03-08 15:18:09 -08:00
else:
words = ["\\Large ``"] + list(self.quote) + ["''"]
2017-03-22 15:06:17 -07:00
quote = TextMobject(*words, **text_mobject_kwargs)
2017-03-08 15:18:09 -08:00
##TODO, make less hacky
if self.quote_arg_separator == " ":
quote[0].shift(0.2*RIGHT)
quote[-1].shift(0.2*LEFT)
2017-03-08 15:18:09 -08:00
for term, color in self.highlighted_quote_terms.items():
quote.highlight_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)
2017-03-08 15:18:09 -08:00
author.next_to(quote, DOWN)
author.highlight(YELLOW)
return author
class PatreonThanks(Scene):
CONFIG = {
2017-11-02 22:58:54 -07:00
"specific_patrons" : [],
"max_patron_group_size" : 20,
2017-07-05 19:21:24 -07:00
"patron_scale_val" : 0.8,
2017-03-08 15:18:09 -08:00
}
def construct(self):
morty = Mortimer()
morty.next_to(ORIGIN, DOWN)
patreon_logo = PatreonLogo()
patreon_logo.to_edge(UP)
2017-03-08 15:18:09 -08:00
n_patrons = len(self.specific_patrons)
2017-04-14 23:30:29 -07:00
patrons = map(TextMobject, self.specific_patrons)
num_groups = float(len(patrons)) / self.max_patron_group_size
2017-04-20 13:30:51 -07:00
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
2017-04-14 23:30:29 -07:00
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,
2017-04-14 23:30:29 -07:00
),
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))
2017-04-14 23:30:29 -07:00
self.play(Blink(morty))
last_group = group
2017-03-08 15:18:09 -08:00
2017-12-08 17:57:32 -08:00
class PatreonEndScreen(PatreonThanks):
CONFIG = {
"n_patron_columns" : 3,
"max_patron_width" : 3,
"run_time" : 20,
2018-03-04 10:43:35 -08:00
"randomize_order" : True,
2017-12-08 17:57:32 -08:00
}
def construct(self):
2018-03-04 10:43:35 -08:00
if self.randomize_order:
random.shuffle(self.specific_patrons)
2017-12-08 17:57:32 -08:00
self.add_title()
self.scroll_through_patrons()
def add_title(self):
2018-03-14 14:52:33 -07:00
title = self.title = TextMobject("Clicky Stuffs")
2017-12-08 17:57:32 -08:00
title.scale(1.5)
title.to_edge(UP, buff = MED_SMALL_BUFF)
randy, morty = 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 = SPACE_WIDTH - logo_box.get_right()[0]
black_rect = Rectangle(
fill_color = BLACK,
fill_opacity = 1,
stroke_width = 0,
width = 2*SPACE_WIDTH,
height = 1.1*SPACE_HEIGHT
)
black_rect.to_edge(UP, buff = 0)
line = DashedLine(SPACE_WIDTH*LEFT, SPACE_WIDTH*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)
2017-12-08 17:57:32 -08:00
columns.to_edge(RIGHT)
self.play(
columns.next_to, SPACE_HEIGHT*DOWN, UP, LARGE_BUFF,
columns.to_edge, RIGHT,
Animation(black_rect),
rate_func = None,
run_time = self.run_time,
)
2017-06-20 14:05:48 -07:00
class ExternallyAnimatedScene(Scene):
def construct(self):
raise Exception("Don't actually run this class.")
2017-03-08 15:18:09 -08:00
2017-08-05 20:47:06 -07:00
class TODOStub(Scene):
CONFIG = {
"message" : ""
}
def construct(self):
self.add(TextMobject("TODO: %s"%self.message))
2018-01-15 19:15:05 -08:00
self.wait()
2017-03-08 15:18:09 -08:00