2016-11-07 11:05:41 -08:00
|
|
|
from helpers import *
|
|
|
|
|
|
|
|
from mobject import Mobject
|
|
|
|
from mobject.vectorized_mobject import VGroup
|
|
|
|
from mobject.svg_mobject import SVGMobject
|
|
|
|
from mobject.tex_mobject import TextMobject
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VideoIcon(SVGMobject):
|
|
|
|
CONFIG = {
|
|
|
|
"file_name" : "video_icon",
|
|
|
|
"width" : 2*SPACE_WIDTH/12.,
|
|
|
|
"considered_smooth" : False,
|
|
|
|
}
|
|
|
|
def __init__(self, **kwargs):
|
|
|
|
SVGMobject.__init__(self, **kwargs)
|
|
|
|
self.center()
|
|
|
|
self.scale_to_fit_width(self.width)
|
|
|
|
self.set_stroke(color = WHITE, width = 0)
|
|
|
|
self.set_fill(color = WHITE, opacity = 1)
|
|
|
|
for mob in self:
|
|
|
|
mob.considered_smooth = False
|
|
|
|
|
|
|
|
class VideoSeries(VGroup):
|
|
|
|
CONFIG = {
|
|
|
|
"num_videos" : 10,
|
|
|
|
"gradient_colors" : [BLUE_B, BLUE_D],
|
|
|
|
}
|
|
|
|
def __init__(self, **kwargs):
|
|
|
|
digest_config(self, kwargs)
|
|
|
|
videos = [VideoIcon() for x in range(self.num_videos)]
|
|
|
|
VGroup.__init__(self, *videos, **kwargs)
|
|
|
|
self.arrange_submobjects()
|
|
|
|
self.gradient_highlight(*self.gradient_colors)
|
|
|
|
|
|
|
|
|
|
|
|
class Headphones(SVGMobject):
|
|
|
|
CONFIG = {
|
|
|
|
"file_name" : "headphones",
|
|
|
|
"height" : 2,
|
|
|
|
"y_stretch_factor" : 0.5,
|
|
|
|
"color" : GREY,
|
|
|
|
}
|
|
|
|
def __init__(self, **kwargs):
|
|
|
|
digest_config(self, kwargs)
|
2016-12-07 18:37:56 -08:00
|
|
|
SVGMobject.__init__(self, file_name = self.file_name, **kwargs)
|
2016-11-07 11:05:41 -08:00
|
|
|
self.stretch(self.y_stretch_factor, 1)
|
|
|
|
self.scale_to_fit_height(self.height)
|
|
|
|
self.set_stroke(width = 0)
|
|
|
|
self.set_fill(color = self.color)
|
|
|
|
|
|
|
|
|
|
|
|
class Bubble(SVGMobject):
|
|
|
|
CONFIG = {
|
|
|
|
"direction" : LEFT,
|
|
|
|
"center_point" : ORIGIN,
|
|
|
|
"content_scale_factor" : 0.75,
|
|
|
|
"height" : 5,
|
|
|
|
"width" : 8,
|
|
|
|
"bubble_center_adjustment_factor" : 1./8,
|
|
|
|
"file_name" : None,
|
|
|
|
"propogate_style_to_family" : True,
|
|
|
|
}
|
|
|
|
def __init__(self, **kwargs):
|
|
|
|
digest_config(self, kwargs, locals())
|
|
|
|
if self.file_name is None:
|
|
|
|
raise Exception("Must invoke Bubble subclass")
|
|
|
|
SVGMobject.__init__(self, **kwargs)
|
|
|
|
self.center()
|
|
|
|
self.stretch_to_fit_height(self.height)
|
|
|
|
self.stretch_to_fit_width(self.width)
|
|
|
|
if self.direction[0] > 0:
|
|
|
|
Mobject.flip(self)
|
|
|
|
self.direction_was_specified = ("direction" in kwargs)
|
|
|
|
self.content = Mobject()
|
|
|
|
|
|
|
|
def get_tip(self):
|
|
|
|
#TODO, find a better way
|
|
|
|
return self.get_corner(DOWN+self.direction)-0.6*self.direction
|
|
|
|
|
|
|
|
def get_bubble_center(self):
|
|
|
|
factor = self.bubble_center_adjustment_factor
|
|
|
|
return self.get_center() + factor*self.get_height()*UP
|
|
|
|
|
|
|
|
def move_tip_to(self, point):
|
2016-11-18 21:23:52 -08:00
|
|
|
VGroup(self, self.content).shift(point - self.get_tip())
|
2016-11-07 11:05:41 -08:00
|
|
|
return self
|
|
|
|
|
|
|
|
def flip(self):
|
|
|
|
Mobject.flip(self)
|
|
|
|
self.direction = -np.array(self.direction)
|
|
|
|
return self
|
|
|
|
|
|
|
|
def pin_to(self, mobject):
|
|
|
|
mob_center = mobject.get_center()
|
|
|
|
want_to_filp = np.sign(mob_center[0]) != np.sign(self.direction[0])
|
|
|
|
can_flip = not self.direction_was_specified
|
|
|
|
if want_to_filp and can_flip:
|
|
|
|
self.flip()
|
|
|
|
boundary_point = mobject.get_critical_point(UP-self.direction)
|
|
|
|
vector_from_center = 1.0*(boundary_point-mob_center)
|
|
|
|
self.move_tip_to(mob_center+vector_from_center)
|
|
|
|
return self
|
|
|
|
|
|
|
|
def position_mobject_inside(self, mobject):
|
|
|
|
scaled_width = self.content_scale_factor*self.get_width()
|
|
|
|
if mobject.get_width() > scaled_width:
|
|
|
|
mobject.scale_to_fit_width(scaled_width)
|
|
|
|
mobject.shift(
|
|
|
|
self.get_bubble_center() - mobject.get_center()
|
|
|
|
)
|
|
|
|
return mobject
|
|
|
|
|
|
|
|
def add_content(self, mobject):
|
|
|
|
self.position_mobject_inside(mobject)
|
|
|
|
self.content = mobject
|
|
|
|
return self.content
|
|
|
|
|
|
|
|
def write(self, *text):
|
|
|
|
self.add_content(TextMobject(*text))
|
|
|
|
return self
|
|
|
|
|
|
|
|
def resize_to_content(self):
|
|
|
|
target_width = self.content.get_width()
|
|
|
|
target_width += max(2*MED_BUFF, 2)
|
|
|
|
target_height = self.content.get_height()
|
|
|
|
target_height += 2.5*LARGE_BUFF
|
|
|
|
tip_point = self.get_tip()
|
|
|
|
self.stretch_to_fit_width(target_width)
|
|
|
|
self.stretch_to_fit_height(target_height)
|
|
|
|
self.move_tip_to(tip_point)
|
|
|
|
self.position_mobject_inside(self.content)
|
|
|
|
|
|
|
|
def clear(self):
|
|
|
|
self.add_content(VMobject())
|
|
|
|
return self
|
|
|
|
|
|
|
|
class SpeechBubble(Bubble):
|
|
|
|
CONFIG = {
|
|
|
|
"file_name" : "Bubbles_speech.svg",
|
|
|
|
"height" : 4
|
|
|
|
}
|
|
|
|
|
|
|
|
class DoubleSpeechBubble(Bubble):
|
|
|
|
CONFIG = {
|
|
|
|
"file_name" : "Bubbles_double_speech.svg",
|
|
|
|
"height" : 4
|
|
|
|
}
|
|
|
|
|
|
|
|
class ThoughtBubble(Bubble):
|
|
|
|
CONFIG = {
|
|
|
|
"file_name" : "Bubbles_thought.svg",
|
|
|
|
}
|
|
|
|
|
|
|
|
def __init__(self, **kwargs):
|
|
|
|
Bubble.__init__(self, **kwargs)
|
|
|
|
self.submobjects.sort(
|
|
|
|
lambda m1, m2 : int((m1.get_bottom()-m2.get_bottom())[1])
|
|
|
|
)
|
|
|
|
|
|
|
|
def make_green_screen(self):
|
|
|
|
self.submobjects[-1].set_fill(GREEN_SCREEN, opacity = 1)
|
|
|
|
return self
|