3b1b-manim/topics/common_scenes.py

226 lines
7.1 KiB
Python
Raw Normal View History

2017-03-08 15:18:09 -08:00
from constants import *
2017-03-08 15:18:09 -08:00
from animation.animation import Animation
from animation.composition import LaggedStart
from animation.creation import DrawBorderThenFill
from animation.creation import Write
from animation.transform import ApplyMethod
from animation.creation import FadeIn
from animation.creation import FadeOut
from mobject.tex_mobject import TexMobject
from mobject.tex_mobject import TextMobject
2017-03-08 15:18:09 -08:00
from mobject.vectorized_mobject import VGroup
from scene.scene import Scene
from topics.characters import Blink
from topics.characters import Mortimer
from topics.characters import Randolph
from topics.geometry import DashedLine
from topics.geometry import Rectangle
from topics.geometry import Square
2017-03-08 15:18:09 -08:00
from topics.objects import PatreonLogo
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 = FRAME_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)
2018-03-30 11:51:31 -07:00
for term, color in self.set_colored_quote_terms.items():
quote.set_color_by_tex(term, color)
2017-03-08 15:18:09 -08:00
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)
2018-03-30 11:51:31 -07:00
author.set_color(YELLOW)
2017-03-08 15:18:09 -08:00
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)
2018-03-22 11:06:20 -07:00
randy, morty = self.pi_creatures = VGroup(Randolph(), Mortimer())
2017-12-08 17:57:32 -08:00
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]
2017-12-08 17:57:32 -08:00
black_rect = Rectangle(
fill_color = BLACK,
fill_opacity = 1,
stroke_width = 0,
width = FRAME_WIDTH,
height = 1.1*FRAME_Y_RADIUS
2017-12-08 17:57:32 -08:00
)
black_rect.to_edge(UP, buff = 0)
line = DashedLine(FRAME_X_RADIUS*LEFT, FRAME_X_RADIUS*RIGHT)
2017-12-08 17:57:32 -08:00
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, FRAME_Y_RADIUS*DOWN, UP, LARGE_BUFF,
2017-12-08 17:57:32 -08:00
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