mirror of
https://github.com/3b1b/manim.git
synced 2025-07-31 22:13:30 +08:00
212 lines
6.5 KiB
Python
212 lines
6.5 KiB
Python
import numpy as np
|
|
import itertools as it
|
|
import os
|
|
|
|
from image_mobject import *
|
|
from mobject import *
|
|
from simple_mobjects import *
|
|
|
|
|
|
class PiCreature(Mobject):
|
|
DEFAULT_COLOR = "blue"
|
|
def __init__(self, **kwargs):
|
|
Mobject.__init__(self, **kwargs)
|
|
color = self.DEFAULT_COLOR
|
|
scale_val = 0.5
|
|
mouth_to_eyes_distance = 0.25
|
|
part_names = [
|
|
'arm',
|
|
'body',
|
|
'left_eye',
|
|
'right_eye',
|
|
'left_leg',
|
|
'right_leg',
|
|
'mouth',
|
|
]
|
|
white_part_names = ['left_eye', 'right_eye', 'mouth']
|
|
directory = os.path.join(IMAGE_DIR, "PiCreature")
|
|
|
|
parts = []
|
|
self.white_parts = []
|
|
for part_name in part_names:
|
|
path = os.path.join(directory, "pi_creature_"+part_name)
|
|
path += ".png"
|
|
mob = ImageMobject(path)
|
|
mob.scale(scale_val)
|
|
if part_name in white_part_names:
|
|
self.white_parts.append(mob)
|
|
else:
|
|
mob.highlight(color)
|
|
setattr(self, part_name, mob)
|
|
parts.append(mob)
|
|
self.mouth.center().shift(
|
|
self.left_eye.get_center()/2 +
|
|
self.right_eye.get_center()/2 -
|
|
(0, mouth_to_eyes_distance, 0)
|
|
)
|
|
self.eyes = [self.left_eye, self.right_eye]
|
|
self.legs = [self.left_leg, self.right_leg]
|
|
for part in parts:
|
|
self.add(part)
|
|
self.parts = parts
|
|
|
|
def rewire_part_attributes(self, self_from_parts = False):
|
|
if self_from_parts:
|
|
total_num_points = sum(map(Mobject.get_num_points, self.parts))
|
|
self.points = np.zeros((total_num_points, Mobject.DIM))
|
|
self.rgbs = np.zeros((total_num_points, Mobject.DIM))
|
|
curr = 0
|
|
for part in self.parts:
|
|
n_points = part.get_num_points()
|
|
if self_from_parts:
|
|
self.points[curr:curr+n_points,:] = part.points
|
|
self.rgbs[curr:curr+n_points,:] = part.rgbs
|
|
else:
|
|
part.points = self.points[curr:curr+n_points,:]
|
|
part.rgbs = self.rgbs[curr:curr+n_points,:]
|
|
curr += n_points
|
|
return self
|
|
|
|
def reload_from_parts(self):
|
|
self.rewire_part_attributes(self_from_parts = True)
|
|
|
|
def highlight(self, color, condition = None):
|
|
self.rewire_part_attributes()
|
|
if condition is not None:
|
|
Mobject.highlight(self, color, condition)
|
|
return self
|
|
for part in set(self.parts).difference(self.white_parts):
|
|
part.highlight(color)
|
|
self.reload_from_parts()
|
|
return self
|
|
|
|
def move_to(self, destination):
|
|
bottom = np.array((
|
|
np.mean(self.points[:,0]),
|
|
min(self.points[:,1]),
|
|
0
|
|
))
|
|
self.shift(destination-bottom)
|
|
self.rewire_part_attributes()
|
|
return self
|
|
|
|
def give_frown(self):
|
|
center = self.mouth.get_center()
|
|
self.mouth.center()
|
|
self.mouth.apply_function(lambda (x, y, z) : (x, -y, z))
|
|
self.mouth.shift(center)
|
|
self.reload_from_parts()
|
|
return self
|
|
|
|
def give_straight_face(self):
|
|
center = self.mouth.get_center()
|
|
self.mouth.center()
|
|
new_mouth = tex_mobject("-").scale(0.5)
|
|
new_mouth.center().shift(self.mouth.get_center())
|
|
new_mouth.shift(center)
|
|
self.parts[self.parts.index(self.mouth)] = new_mouth
|
|
self.white_parts[self.white_parts.index(self.mouth)] = new_mouth
|
|
self.mouth = new_mouth
|
|
self.reload_from_parts()
|
|
return self
|
|
|
|
def get_eye_center(self):
|
|
return center_of_mass([
|
|
self.left_eye.get_center(),
|
|
self.right_eye.get_center()
|
|
]) + 0.04*RIGHT + 0.02*UP
|
|
|
|
def make_mean(self):
|
|
eye_x, eye_y = self.get_eye_center()[:2]
|
|
def should_delete((x, y, z)):
|
|
return y - eye_y > 0.3*abs(x - eye_x)
|
|
for eye in self.left_eye, self.right_eye:
|
|
eye.highlight("black", should_delete)
|
|
self.give_straight_face()
|
|
return self
|
|
|
|
def make_sad(self):
|
|
eye_x, eye_y = self.get_eye_center()[:2]
|
|
eye_y += 0.15
|
|
def should_delete((x, y, z)):
|
|
return y - eye_y > -0.3*abs(x - eye_x)
|
|
for eye in self.left_eye, self.right_eye:
|
|
eye.highlight("black", should_delete)
|
|
self.give_frown()
|
|
self.reload_from_parts()
|
|
return self
|
|
|
|
def get_step_intermediate(self, pi_creature):
|
|
vect = pi_creature.get_center() - self.get_center()
|
|
result = deepcopy(self).shift(vect / 2.0)
|
|
result.rewire_part_attributes()
|
|
left_forward = vect[0] > 0
|
|
if self.right_leg.get_center()[0] < self.left_leg.get_center()[0]:
|
|
#For Mortimer's case
|
|
left_forward = not left_forward
|
|
if left_forward:
|
|
result.left_leg.wag(vect/2.0, DOWN)
|
|
result.right_leg.wag(-vect/2.0, DOWN)
|
|
else:
|
|
result.right_leg.wag(vect/2.0, DOWN)
|
|
result.left_leg.wag(-vect/2.0, DOWN)
|
|
return result
|
|
|
|
def blink(self):
|
|
for eye in self.left_eye, self.right_eye:
|
|
bottom = min(eye.points[:,1])
|
|
eye.apply_function(
|
|
lambda (x, y, z) : (x, bottom, z)
|
|
)
|
|
self.reload_from_parts()
|
|
return self
|
|
|
|
def shift_eyes(self):
|
|
for eye in self.left_eye, self.right_eye:
|
|
center = eye.get_center()
|
|
eye.center()
|
|
eye.rotate(np.pi, UP)
|
|
eye.shift(center)
|
|
self.reload_from_parts()
|
|
return self
|
|
|
|
def to_symbol(self):
|
|
for white_part in self.white_parts:
|
|
self.parts.remove(white_part)
|
|
self.reload_from_parts()
|
|
return self
|
|
|
|
|
|
class Randolph(PiCreature):
|
|
pass #Nothing more than an alternative name
|
|
|
|
class Mortimer(PiCreature):
|
|
DEFAULT_COLOR = DARK_BROWN
|
|
def __init__(self, *args, **kwargs):
|
|
PiCreature.__init__(self, *args, **kwargs)
|
|
# self.highlight(DARK_BROWN)
|
|
self.give_straight_face()
|
|
self.rotate(np.pi, UP)
|
|
self.rewire_part_attributes()
|
|
|
|
class TauCreature(PiCreature):
|
|
def __init__(self, **kwargs):
|
|
leg_shift_val = 0.25
|
|
leg_wag_val = 0.2
|
|
PiCreature.__init__(self, **kwargs)
|
|
self.parts.remove(self.right_leg)
|
|
self.left_leg.shift(leg_shift_val*RIGHT)
|
|
self.left_leg.wag(leg_wag_val*RIGHT, DOWN)
|
|
self.leg = self.left_leg
|
|
self.reload_from_parts()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|