mirror of
https://github.com/3b1b/manim.git
synced 2025-04-13 09:47:07 +00:00
228 lines
6.4 KiB
Python
228 lines
6.4 KiB
Python
from helpers import *
|
|
|
|
from mobject import Mobject
|
|
from mobject.svg_mobject import SVGMobject
|
|
from mobject.vectorized_mobject import VMobject
|
|
from mobject.tex_mobject import TextMobject
|
|
|
|
from animation.transform import ApplyMethod
|
|
|
|
|
|
PI_CREATURE_DIR = os.path.join(IMAGE_DIR, "PiCreature")
|
|
PI_CREATURE_SCALE_VAL = 0.5
|
|
|
|
MOUTH_INDEX = 5
|
|
BODY_INDEX = 4
|
|
RIGHT_PUPIL_INDEX = 3
|
|
LEFT_PUPIL_INDEX = 2
|
|
RIGHT_EYE_INDEX = 1
|
|
LEFT_EYE_INDEX = 0
|
|
|
|
|
|
class PiCreature(SVGMobject):
|
|
CONFIG = {
|
|
"color" : BLUE_E,
|
|
"stroke_width" : 0,
|
|
"fill_opacity" : 1.0,
|
|
"initial_scale_val" : 0.01,
|
|
"corner_scale_factor" : 0.75,
|
|
}
|
|
def __init__(self, mode = "plain", **kwargs):
|
|
self.parts_named = False
|
|
svg_file = os.path.join(
|
|
PI_CREATURE_DIR,
|
|
"PiCreatures_%s.svg"%mode
|
|
)
|
|
digest_config(self, kwargs, locals())
|
|
SVGMobject.__init__(self, svg_file, **kwargs)
|
|
self.init_colors()
|
|
|
|
def name_parts(self):
|
|
self.mouth = self.submobjects[MOUTH_INDEX]
|
|
self.body = self.submobjects[BODY_INDEX]
|
|
self.pupils = VMobject(*[
|
|
self.submobjects[LEFT_PUPIL_INDEX],
|
|
self.submobjects[RIGHT_PUPIL_INDEX]
|
|
])
|
|
self.eyes = VMobject(*[
|
|
self.submobjects[LEFT_EYE_INDEX],
|
|
self.submobjects[RIGHT_EYE_INDEX]
|
|
])
|
|
self.submobjects = []
|
|
self.add(self.body, self.mouth, self.eyes, self.pupils)
|
|
self.parts_named = True
|
|
|
|
def init_colors(self):
|
|
self.set_stroke(color = BLACK, width = self.stroke_width)
|
|
if not self.parts_named:
|
|
self.name_parts()
|
|
self.mouth.set_fill(BLACK, opacity = 1)
|
|
self.body.set_fill(self.color, opacity = 1)
|
|
self.pupils.set_fill(BLACK, opacity = 1)
|
|
self.eyes.set_fill(WHITE, opacity = 1)
|
|
return self
|
|
|
|
|
|
def highlight(self, color):
|
|
self.body.set_fill(color)
|
|
return self
|
|
|
|
def move_to(self, destination):
|
|
self.shift(destination-self.get_bottom())
|
|
return self
|
|
|
|
def change_mode(self, mode):
|
|
curr_center = self.get_center()
|
|
curr_height = self.get_height()
|
|
flip = self.is_flipped()
|
|
self.__class__.__init__(self, mode)
|
|
self.scale_to_fit_height(curr_height)
|
|
self.shift(curr_center)
|
|
if flip:
|
|
self.flip()
|
|
return self
|
|
|
|
def look_left(self):
|
|
self.change_mode(self.mode + "_looking_left")
|
|
return self
|
|
|
|
def is_flipped(self):
|
|
return self.eyes.submobjects[0].get_center()[0] > \
|
|
self.eyes.submobjects[1].get_center()[0]
|
|
|
|
def blink(self):
|
|
eye_bottom_y = self.eyes.get_bottom()[1]
|
|
for mob in self.eyes, self.pupils:
|
|
mob.apply_function(
|
|
lambda p : [p[0], eye_bottom_y, p[2]]
|
|
)
|
|
return self
|
|
|
|
def to_corner(self, vect = None):
|
|
if vect is not None:
|
|
SVGMobject.to_corner(self, vect)
|
|
else:
|
|
self.scale(self.corner_scale_factor)
|
|
self.to_corner(DOWN+LEFT)
|
|
return self
|
|
|
|
def get_bubble(self, bubble_type = "thought", **kwargs):
|
|
if bubble_type == "thought":
|
|
bubble = ThoughtBubble(**kwargs)
|
|
elif bubble_type == "speech":
|
|
bubble = SpeechBubble(**kwargs)
|
|
else:
|
|
raise Exception("%s is an invalid bubble type"%bubble_type)
|
|
bubble.pin_to(self)
|
|
return bubble
|
|
|
|
|
|
class Randolph(PiCreature):
|
|
pass #Nothing more than an alternative name
|
|
|
|
class Mortimer(PiCreature):
|
|
CONFIG = {
|
|
"color" : "#736357"
|
|
}
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
PiCreature.__init__(self, *args, **kwargs)
|
|
self.flip()
|
|
|
|
|
|
class Mathematician(PiCreature):
|
|
CONFIG = {
|
|
"color" : GREY,
|
|
}
|
|
|
|
class Blink(ApplyMethod):
|
|
CONFIG = {
|
|
"rate_func" : squish_rate_func(there_and_back)
|
|
}
|
|
def __init__(self, pi_creature, **kwargs):
|
|
ApplyMethod.__init__(self, pi_creature.blink, **kwargs)
|
|
|
|
class Bubble(SVGMobject):
|
|
CONFIG = {
|
|
"direction" : LEFT,
|
|
"center_point" : ORIGIN,
|
|
"content_scale_factor" : 0.75,
|
|
"height" : 5,
|
|
"width" : 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")
|
|
svg_file = os.path.join(
|
|
IMAGE_DIR, self.file_name
|
|
)
|
|
SVGMobject.__init__(self, svg_file, **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.content = Mobject()
|
|
|
|
def get_tip(self):
|
|
return self.get_corner(DOWN+self.direction)
|
|
|
|
def get_bubble_center(self):
|
|
return self.get_center() + self.get_height()*UP/8.0
|
|
|
|
def move_tip_to(self, point):
|
|
self.shift(point - self.get_tip())
|
|
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()
|
|
if (mob_center[0] > 0) != (self.direction[0] > 0):
|
|
self.flip()
|
|
boundary_point = mobject.get_boundary_point(UP-self.direction)
|
|
vector_from_center = 1.2*(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):
|
|
if self.content in self.submobjects:
|
|
self.submobjects.remove(self.content)
|
|
self.position_mobject_inside(mobject)
|
|
self.content = mobject
|
|
self.add(self.content)
|
|
return self
|
|
|
|
def write(self, text):
|
|
self.add_content(TextMobject(text))
|
|
return self
|
|
|
|
def clear(self):
|
|
self.add_content(VMobject())
|
|
return self
|
|
|
|
class SpeechBubble(Bubble):
|
|
CONFIG = {
|
|
"file_name" : "Bubbles_speech.svg",
|
|
}
|
|
|
|
class ThoughtBubble(Bubble):
|
|
CONFIG = {
|
|
"file_name" : "Bubbles_thought.svg",
|
|
}
|
|
|