Pulled pi_creature constructs out into their own folder, so as to note some distinction from the remainder of the library

This commit is contained in:
Grant Sanderson 2018-03-31 16:07:24 -07:00
parent 846bd1065c
commit b757f8b422
12 changed files with 778 additions and 793 deletions

View file

@ -39,6 +39,10 @@ from mobject.svg_mobject import *
from mobject.tex_mobject import *
from mobject.vectorized_mobject import *
from pi_creature.pi_creature import *
from pi_creature.pi_creature_animations import *
from pi_creature.pi_creature_scene import *
from scene.moving_camera_scene import *
from scene.reconfigurable_scene import *
from scene.scene import *
@ -46,7 +50,6 @@ from scene.scene_from_video import *
from scene.zoomed_scene import *
from topics.arithmetic import *
from topics.characters import *
from topics.combinatorics import *
from topics.common_scenes import *
from topics.complex_numbers import *

0
pi_creature/__init__.py Normal file
View file

316
pi_creature/pi_creature.py Normal file
View file

@ -0,0 +1,316 @@
import numpy as np
from constants import *
from mobject.mobject import Mobject
from mobject.svg_mobject import SVGMobject
from mobject.tex_mobject import TextMobject
from mobject.vectorized_mobject import VGroup
from mobject.vectorized_mobject import VMobject
from topics.objects import ThoughtBubble
from animation.transform import Transform
from utils.config_ops import digest_config
from utils.rate_functions import squish_rate_func
from utils.rate_functions import there_and_back
PI_CREATURE_DIR = os.path.join(MEDIA_DIR, "designs", "PiCreature")
PI_CREATURE_SCALE_FACTOR = 0.5
LEFT_EYE_INDEX = 0
RIGHT_EYE_INDEX = 1
LEFT_PUPIL_INDEX = 2
RIGHT_PUPIL_INDEX = 3
BODY_INDEX = 4
MOUTH_INDEX = 5
class PiCreature(SVGMobject):
CONFIG = {
"color" : BLUE_E,
"file_name_prefix" : "PiCreatures",
"stroke_width" : 0,
"stroke_color" : BLACK,
"fill_opacity" : 1.0,
"propagate_style_to_family" : True,
"height" : 3,
"corner_scale_factor" : 0.75,
"flip_at_start" : False,
"is_looking_direction_purposeful" : False,
"start_corner" : None,
#Range of proportions along body where arms are
"right_arm_range" : [0.55, 0.7],
"left_arm_range" : [.34, .462],
}
def __init__(self, mode = "plain", **kwargs):
digest_config(self, kwargs)
self.parts_named = False
try:
svg_file = os.path.join(
PI_CREATURE_DIR,
"%s_%s.svg"%(self.file_name_prefix, mode)
)
SVGMobject.__init__(self, file_name = svg_file, **kwargs)
except:
warnings.warn("No %s design with mode %s"%(self.file_name_prefix, mode))
svg_file = os.path.join(
FILE_DIR,
"PiCreatures_plain.svg",
)
SVGMobject.__init__(self, file_name = svg_file, **kwargs)
if self.flip_at_start:
self.flip()
if self.start_corner is not None:
self.to_corner(self.start_corner)
def name_parts(self):
self.mouth = self.submobjects[MOUTH_INDEX]
self.body = self.submobjects[BODY_INDEX]
self.pupils = VGroup(*[
self.submobjects[LEFT_PUPIL_INDEX],
self.submobjects[RIGHT_PUPIL_INDEX]
])
self.eyes = VGroup(*[
self.submobjects[LEFT_EYE_INDEX],
self.submobjects[RIGHT_EYE_INDEX]
])
self.eye_parts = VGroup(self.eyes, self.pupils)
self.parts_named = True
def init_colors(self):
SVGMobject.init_colors(self)
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 copy(self):
copy_mobject = SVGMobject.copy(self)
copy_mobject.name_parts()
return copy_mobject
def set_color(self, color):
self.body.set_fill(color)
return self
def change_mode(self, mode):
new_self = self.__class__(
mode = mode,
color = self.color
)
new_self.scale_to_fit_height(self.get_height())
if self.is_flipped() ^ new_self.is_flipped():
new_self.flip()
new_self.shift(self.eyes.get_center() - new_self.eyes.get_center())
if hasattr(self, "purposeful_looking_direction"):
new_self.look(self.purposeful_looking_direction)
Transform(self, new_self).update(1)
return self
def look(self, direction):
norm = np.linalg.norm(direction)
if norm == 0:
return
direction /= norm
self.purposeful_looking_direction = direction
for pupil, eye in zip(self.pupils.split(), self.eyes.split()):
pupil_radius = pupil.get_width()/2.
eye_radius = eye.get_width()/2.
pupil.move_to(eye)
if direction[1] < 0:
pupil.shift(pupil_radius*DOWN/3)
pupil.shift(direction*(eye_radius-pupil_radius))
bottom_diff = eye.get_bottom()[1] - pupil.get_bottom()[1]
if bottom_diff > 0:
pupil.shift(bottom_diff*UP)
#TODO, how to handle looking up...
# top_diff = eye.get_top()[1]-pupil.get_top()[1]
# if top_diff < 0:
# pupil.shift(top_diff*UP)
return self
def look_at(self, point_or_mobject):
if isinstance(point_or_mobject, Mobject):
point = point_or_mobject.get_center()
else:
point = point_or_mobject
self.look(point - self.eyes.get_center())
return self
def change(self, new_mode, look_at_arg = None):
self.change_mode(new_mode)
if look_at_arg is not None:
self.look_at(look_at_arg)
return self
def get_looking_direction(self):
return np.sign(np.round(
self.pupils.get_center() - self.eyes.get_center(),
decimals = 2
))
def is_flipped(self):
return self.eyes.submobjects[0].get_center()[0] > \
self.eyes.submobjects[1].get_center()[0]
def blink(self):
eye_parts = self.eye_parts
eye_bottom_y = eye_parts.get_bottom()[1]
eye_parts.apply_function(
lambda p : [p[0], eye_bottom_y, p[2]]
)
return self
def to_corner(self, vect = None, **kwargs):
if vect is not None:
SVGMobject.to_corner(self, vect, **kwargs)
else:
self.scale(self.corner_scale_factor)
self.to_corner(DOWN+LEFT, **kwargs)
return self
def get_bubble(self, *content, **kwargs):
bubble_class = kwargs.get("bubble_class", ThoughtBubble)
bubble = bubble_class(**kwargs)
if len(content) > 0:
if isinstance(content[0], str):
content_mob = TextMobject(*content)
else:
content_mob = content[0]
bubble.add_content(content_mob)
if "height" not in kwargs and "width" not in kwargs:
bubble.resize_to_content()
bubble.pin_to(self)
self.bubble = bubble
return bubble
def make_eye_contact(self, pi_creature):
self.look_at(pi_creature.eyes)
pi_creature.look_at(self.eyes)
return self
def shrug(self):
self.change_mode("shruggie")
top_mouth_point, bottom_mouth_point = [
self.mouth.points[np.argmax(self.mouth.points[:,1])],
self.mouth.points[np.argmin(self.mouth.points[:,1])]
]
self.look(top_mouth_point - bottom_mouth_point)
return self
def get_arm_copies(self):
body = self.body
return VGroup(*[
body.copy().pointwise_become_partial(body, *alpha_range)
for alpha_range in self.right_arm_range, self.left_arm_range
])
def get_all_pi_creature_modes():
result = []
prefix = "%s_"%PiCreature.CONFIG["file_name_prefix"]
suffix = ".svg"
for file in os.listdir(PI_CREATURE_DIR):
if file.startswith(prefix) and file.endswith(suffix):
result.append(
file[len(prefix):-len(suffix)]
)
return result
class Randolph(PiCreature):
pass #Nothing more than an alternative name
class Mortimer(PiCreature):
CONFIG = {
"color" : GREY_BROWN,
"flip_at_start" : True,
}
class Mathematician(PiCreature):
CONFIG = {
"color" : GREY,
}
class BabyPiCreature(PiCreature):
CONFIG = {
"scale_factor" : 0.5,
"eye_scale_factor" : 1.2,
"pupil_scale_factor" : 1.3
}
def __init__(self, *args, **kwargs):
PiCreature.__init__(self, *args, **kwargs)
self.scale(self.scale_factor)
self.shift(LEFT)
self.to_edge(DOWN, buff = LARGE_BUFF)
eyes = VGroup(self.eyes, self.pupils)
eyes_bottom = eyes.get_bottom()
eyes.scale(self.eye_scale_factor)
eyes.move_to(eyes_bottom, aligned_edge = DOWN)
looking_direction = self.get_looking_direction()
for pupil in self.pupils:
pupil.scale_in_place(self.pupil_scale_factor)
self.look(looking_direction)
class TauCreature(PiCreature):
CONFIG = {
"file_name_prefix" : "TauCreatures"
}
class ThreeLeggedPiCreature(PiCreature):
CONFIG = {
"file_name_prefix" : "ThreeLeggedPiCreatures"
}
class Eyes(VMobject):
CONFIG = {
"height" : 0.3,
"thing_looked_at" : None,
"mode" : "plain",
}
def __init__(self, mobject, **kwargs):
VMobject.__init__(self, **kwargs)
self.mobject = mobject
self.submobjects = self.get_eyes().submobjects
def get_eyes(self, mode = None, thing_to_look_at = None):
mode = mode or self.mode
if thing_to_look_at is None:
thing_to_look_at = self.thing_looked_at
pi = Randolph(mode = mode)
eyes = VGroup(pi.eyes, pi.pupils)
pi.scale(self.height/eyes.get_height())
if self.submobjects:
eyes.move_to(self, DOWN)
else:
eyes.move_to(self.mobject.get_top(), DOWN)
if thing_to_look_at is not None:
pi.look_at(thing_to_look_at)
return eyes
def change_mode_anim(self, mode, **kwargs):
self.mode = mode
return Transform(self, self.get_eyes(mode = mode), **kwargs)
def look_at_anim(self, point_or_mobject, **kwargs):
self.thing_looked_at = point_or_mobject
return Transform(
self, self.get_eyes(thing_to_look_at = point_or_mobject),
**kwargs
)
def blink_anim(self, **kwargs):
target = self.copy()
bottom_y = self.get_bottom()[1]
for submob in target:
submob.apply_function(
lambda p : [p[0], bottom_y, p[2]]
)
if "rate_func" not in kwargs:
kwargs["rate_func"] = squish_rate_func(there_and_back)
return Transform(self, target, **kwargs)

View file

@ -0,0 +1,96 @@
from __future__ import absolute_import
from constants import *
from mobject.mobject import Group
from topics.objects import SpeechBubble
from animation.creation import ShowCreation
from animation.creation import Write
from animation.composition import AnimationGroup
from animation.transform import ApplyMethod
from animation.creation import FadeOut
from animation.transform import MoveToTarget
from utils.config_ops import digest_config
from utils.rate_functions import squish_rate_func
from utils.rate_functions import there_and_back
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 PiCreatureBubbleIntroduction(AnimationGroup):
CONFIG = {
"target_mode" : "speaking",
"bubble_class" : SpeechBubble,
"change_mode_kwargs" : {},
"bubble_creation_class" : ShowCreation,
"bubble_creation_kwargs" : {},
"bubble_kwargs" : {},
"content_introduction_class" : Write,
"content_introduction_kwargs" : {},
"look_at_arg" : None,
}
def __init__(self, pi_creature, *content, **kwargs):
digest_config(self, kwargs)
bubble = pi_creature.get_bubble(
*content,
bubble_class = self.bubble_class,
**self.bubble_kwargs
)
Group(bubble, bubble.content).shift_onto_screen()
pi_creature.generate_target()
pi_creature.target.change_mode(self.target_mode)
if self.look_at_arg is not None:
pi_creature.target.look_at(self.look_at_arg)
change_mode = MoveToTarget(pi_creature, **self.change_mode_kwargs)
bubble_creation = self.bubble_creation_class(
bubble, **self.bubble_creation_kwargs
)
content_introduction = self.content_introduction_class(
bubble.content, **self.content_introduction_kwargs
)
AnimationGroup.__init__(
self, change_mode, bubble_creation, content_introduction,
**kwargs
)
class PiCreatureSays(PiCreatureBubbleIntroduction):
CONFIG = {
"target_mode" : "speaking",
"bubble_class" : SpeechBubble,
}
class RemovePiCreatureBubble(AnimationGroup):
CONFIG = {
"target_mode" : "plain",
"look_at_arg" : None,
"remover" : True,
}
def __init__(self, pi_creature, **kwargs):
assert hasattr(pi_creature, "bubble")
digest_config(self, kwargs, locals())
pi_creature.generate_target()
pi_creature.target.change_mode(self.target_mode)
if self.look_at_arg is not None:
pi_creature.target.look_at(self.look_at_arg)
AnimationGroup.__init__(
self,
MoveToTarget(pi_creature),
FadeOut(pi_creature.bubble),
FadeOut(pi_creature.bubble.content),
)
def clean_up(self, surrounding_scene = None):
AnimationGroup.clean_up(self, surrounding_scene)
self.pi_creature.bubble = None
if surrounding_scene is not None:
surrounding_scene.add(self.pi_creature)

View file

@ -0,0 +1,362 @@
from __future__ import absolute_import
import itertools as it
import numpy as np
import random
from constants import *
from mobject.vectorized_mobject import VGroup
from topics.geometry import ScreenRectangle
from topics.objects import SpeechBubble
from topics.objects import ThoughtBubble
from animation.transform import ApplyMethod
from animation.transform import ReplacementTransform
from animation.transform import Transform
from pi_creature.pi_creature import PiCreature
from pi_creature.pi_creature import Mortimer
from pi_creature.pi_creature import Randolph
from pi_creature.pi_creature_animations import Blink
from pi_creature.pi_creature_animations import PiCreatureBubbleIntroduction
from pi_creature.pi_creature_animations import RemovePiCreatureBubble
from scene.scene import Scene
from utils.rate_functions import squish_rate_func
from utils.rate_functions import there_and_back
class PiCreatureScene(Scene):
CONFIG = {
"total_wait_time" : 0,
"seconds_to_blink" : 3,
"pi_creatures_start_on_screen" : True,
"default_pi_creature_kwargs" : {
"color" : GREY_BROWN,
"flip_at_start" : True,
},
"default_pi_creature_start_corner" : DOWN+LEFT,
}
def setup(self):
self.pi_creatures = self.create_pi_creatures()
self.pi_creature = self.get_primary_pi_creature()
if self.pi_creatures_start_on_screen:
self.add(*self.pi_creatures)
def create_pi_creatures(self):
"""
Likely updated for subclasses
"""
return VGroup(self.create_pi_creature())
def create_pi_creature(self):
pi_creature = PiCreature(**self.default_pi_creature_kwargs)
pi_creature.to_corner(self.default_pi_creature_start_corner)
return pi_creature
def get_pi_creatures(self):
return self.pi_creatures
def get_primary_pi_creature(self):
return self.pi_creatures[0]
def any_pi_creatures_on_screen(self):
mobjects = self.get_mobjects()
return any([pi in mobjects for pi in self.get_pi_creatures()])
def get_on_screen_pi_creatures(self):
mobjects = self.get_mobjects()
return VGroup(*filter(
lambda pi : pi in mobjects,
self.get_pi_creatures()
))
def introduce_bubble(self, *args, **kwargs):
if isinstance(args[0], PiCreature):
pi_creature = args[0]
content = args[1:]
else:
pi_creature = self.get_primary_pi_creature()
content = args
bubble_class = kwargs.pop("bubble_class", SpeechBubble)
target_mode = kwargs.pop(
"target_mode",
"thinking" if bubble_class is ThoughtBubble else "speaking"
)
bubble_kwargs = kwargs.pop("bubble_kwargs", {})
bubble_removal_kwargs = kwargs.pop("bubble_removal_kwargs", {})
added_anims = kwargs.pop("added_anims", [])
anims = []
on_screen_mobjects = self.camera.extract_mobject_family_members(
self.get_mobjects()
)
def has_bubble(pi):
return hasattr(pi, "bubble") and \
pi.bubble is not None and \
pi.bubble in on_screen_mobjects
pi_creatures_with_bubbles = filter(has_bubble, self.get_pi_creatures())
if pi_creature in pi_creatures_with_bubbles:
pi_creatures_with_bubbles.remove(pi_creature)
old_bubble = pi_creature.bubble
bubble = pi_creature.get_bubble(
*content,
bubble_class = bubble_class,
**bubble_kwargs
)
anims += [
ReplacementTransform(old_bubble, bubble),
ReplacementTransform(old_bubble.content, bubble.content),
pi_creature.change_mode, target_mode
]
else:
anims.append(PiCreatureBubbleIntroduction(
pi_creature,
*content,
bubble_class = bubble_class,
bubble_kwargs = bubble_kwargs,
target_mode = target_mode,
**kwargs
))
anims += [
RemovePiCreatureBubble(pi, **bubble_removal_kwargs)
for pi in pi_creatures_with_bubbles
]
anims += added_anims
self.play(*anims, **kwargs)
def pi_creature_says(self, *args, **kwargs):
self.introduce_bubble(
*args,
bubble_class = SpeechBubble,
**kwargs
)
def pi_creature_thinks(self, *args, **kwargs):
self.introduce_bubble(
*args,
bubble_class = ThoughtBubble,
**kwargs
)
def say(self, *content, **kwargs):
self.pi_creature_says(self.get_primary_pi_creature(), *content, **kwargs)
def think(self, *content, **kwargs):
self.pi_creature_thinks(self.get_primary_pi_creature(), *content, **kwargs)
def compile_play_args_to_animation_list(self, *args):
"""
Add animations so that all pi creatures look at the
first mobject being animated with each .play call
"""
animations = Scene.compile_play_args_to_animation_list(self, *args)
if not self.any_pi_creatures_on_screen():
return animations
non_pi_creature_anims = filter(
lambda anim : anim.mobject not in self.get_pi_creatures(),
animations
)
if len(non_pi_creature_anims) == 0:
return animations
first_anim = non_pi_creature_anims[0]
#Look at ending state
first_anim.update(1)
point_of_interest = first_anim.mobject.get_center()
first_anim.update(0)
for pi_creature in self.get_pi_creatures():
if pi_creature not in self.get_mobjects():
continue
if pi_creature in first_anim.mobject.submobject_family():
continue
anims_with_pi_creature = filter(
lambda anim : pi_creature in anim.mobject.submobject_family(),
animations
)
for anim in anims_with_pi_creature:
if isinstance(anim, Transform):
index = anim.mobject.submobject_family().index(pi_creature)
target_family = anim.target_mobject.submobject_family()
target = target_family[index]
if isinstance(target, PiCreature):
target.look_at(point_of_interest)
if not anims_with_pi_creature:
animations.append(
ApplyMethod(pi_creature.look_at, point_of_interest)
)
return animations
def blink(self):
self.play(Blink(random.choice(self.get_on_screen_pi_creatures())))
def joint_blink(self, pi_creatures = None, shuffle = True, **kwargs):
if pi_creatures is None:
pi_creatures = self.get_on_screen_pi_creatures()
creatures_list = list(pi_creatures)
if shuffle:
random.shuffle(creatures_list)
def get_rate_func(pi):
index = creatures_list.index(pi)
proportion = float(index)/len(creatures_list)
start_time = 0.8*proportion
return squish_rate_func(
there_and_back,
start_time, start_time + 0.2
)
self.play(*[
Blink(pi, rate_func = get_rate_func(pi), **kwargs)
for pi in creatures_list
])
return self
def wait(self, time = 1, blink = True):
while time >= 1:
time_to_blink = self.total_wait_time%self.seconds_to_blink == 0
if blink and self.any_pi_creatures_on_screen() and time_to_blink:
self.blink()
self.num_plays -= 1 #This shouldn't count as an animation
else:
self.non_blink_wait()
time -= 1
self.total_wait_time += 1
if time > 0:
self.non_blink_wait(time)
return self
def non_blink_wait(self, time = 1):
Scene.wait(self, time)
return self
def change_mode(self, mode):
self.play(self.get_primary_pi_creature().change_mode, mode)
def look_at(self, thing_to_look_at, pi_creatures = None):
if pi_creatures is None:
pi_creatures = self.get_pi_creatures()
self.play(*it.chain(*[
[pi.look_at, thing_to_look_at]
for pi in pi_creatures
]))
class TeacherStudentsScene(PiCreatureScene):
CONFIG = {
"student_colors" : [BLUE_D, BLUE_E, BLUE_C],
"student_scale_factor" : 0.8,
"seconds_to_blink" : 2,
"screen_height" : 3,
}
def setup(self):
PiCreatureScene.setup(self)
self.screen = ScreenRectangle(height = self.screen_height)
self.screen.to_corner(UP+LEFT)
self.hold_up_spot = self.teacher.get_corner(UP+LEFT) + MED_LARGE_BUFF*UP
def create_pi_creatures(self):
self.teacher = Mortimer()
self.teacher.to_corner(DOWN + RIGHT)
self.teacher.look(DOWN+LEFT)
self.students = VGroup(*[
Randolph(color = c)
for c in self.student_colors
])
self.students.arrange_submobjects(RIGHT)
self.students.scale(self.student_scale_factor)
self.students.to_corner(DOWN+LEFT)
self.teacher.look_at(self.students[-1].eyes)
for student in self.students:
student.look_at(self.teacher.eyes)
return [self.teacher] + list(self.students)
def get_teacher(self):
return self.teacher
def get_students(self):
return self.students
def teacher_says(self, *content, **kwargs):
return self.pi_creature_says(
self.get_teacher(), *content, **kwargs
)
def student_says(self, *content, **kwargs):
if "target_mode" not in kwargs:
target_mode = random.choice([
"raise_right_hand",
"raise_left_hand",
])
kwargs["target_mode"] = target_mode
student = self.get_students()[kwargs.get("student_index", 1)]
return self.pi_creature_says(
student, *content, **kwargs
)
def teacher_thinks(self, *content, **kwargs):
return self.pi_creature_thinks(
self.get_teacher(), *content, **kwargs
)
def student_thinks(self, *content, **kwargs):
student = self.get_students()[kwargs.get("student_index", 1)]
return self.pi_creature_thinks(student, *content, **kwargs)
def change_all_student_modes(self, mode, **kwargs):
self.change_student_modes(*[mode]*len(self.students), **kwargs)
def change_student_modes(self, *modes, **kwargs):
added_anims = kwargs.pop("added_anims", [])
self.play(
self.get_student_changes(*modes, **kwargs),
*added_anims
)
def get_student_changes(self, *modes, **kwargs):
pairs = zip(self.get_students(), modes)
pairs = [(s, m) for s, m in pairs if m is not None]
start = VGroup(*[s for s, m in pairs])
target = VGroup(*[s.copy().change_mode(m) for s, m in pairs])
if "look_at_arg" in kwargs:
for pi in target:
pi.look_at(kwargs["look_at_arg"])
submobject_mode = kwargs.get("submobject_mode", "lagged_start")
return Transform(
start, target,
submobject_mode = submobject_mode,
run_time = 2
)
def zoom_in_on_thought_bubble(self, bubble = None, radius = FRAME_Y_RADIUS+FRAME_X_RADIUS):
if bubble is None:
for pi in self.get_pi_creatures():
if hasattr(pi, "bubble") and isinstance(pi.bubble, ThoughtBubble):
bubble = pi.bubble
break
if bubble is None:
raise Exception("No pi creatures have a thought bubble")
vect = -bubble.get_bubble_center()
def func(point):
centered = point+vect
return radius*centered/np.linalg.norm(centered)
self.play(*[
ApplyPointwiseFunction(func, mob)
for mob in self.get_mobjects()
])
def teacher_holds_up(self, mobject, target_mode = "raise_right_hand", **kwargs):
mobject.move_to(self.hold_up_spot, DOWN)
mobject.shift_onto_screen()
mobject_copy = mobject.copy()
mobject_copy.shift(DOWN)
mobject_copy.fade(1)
self.play(
ReplacementTransform(mobject_copy, mobject),
self.teacher.change, target_mode,
)

View file

@ -1,762 +0,0 @@
import itertools as it
import numpy as np
import random
from constants import *
from mobject.mobject import Group
from mobject.mobject import Mobject
from mobject.svg_mobject import SVGMobject
from mobject.tex_mobject import TexMobject
from mobject.tex_mobject import TextMobject
from mobject.vectorized_mobject import VGroup
from mobject.vectorized_mobject import VMobject
from topics.geometry import ScreenRectangle
from topics.objects import Bubble
from topics.objects import SpeechBubble
from topics.objects import ThoughtBubble
from animation.animation import Animation
from animation.composition import AnimationGroup
from animation.creation import ShowCreation
from animation.creation import Write
from animation.transform import ApplyMethod
from animation.creation import FadeIn
from animation.creation import FadeOut
from animation.transform import MoveToTarget
from animation.transform import ReplacementTransform
from animation.transform import Transform
from scene.scene import Scene
from utils.config_ops import digest_config
from utils.rate_functions import squish_rate_func
from utils.rate_functions import there_and_back
PI_CREATURE_DIR = os.path.join(MEDIA_DIR, "designs", "PiCreature")
PI_CREATURE_SCALE_FACTOR = 0.5
LEFT_EYE_INDEX = 0
RIGHT_EYE_INDEX = 1
LEFT_PUPIL_INDEX = 2
RIGHT_PUPIL_INDEX = 3
BODY_INDEX = 4
MOUTH_INDEX = 5
class PiCreature(SVGMobject):
CONFIG = {
"color" : BLUE_E,
"file_name_prefix" : "PiCreatures",
"stroke_width" : 0,
"stroke_color" : BLACK,
"fill_opacity" : 1.0,
"propagate_style_to_family" : True,
"height" : 3,
"corner_scale_factor" : 0.75,
"flip_at_start" : False,
"is_looking_direction_purposeful" : False,
"start_corner" : None,
#Range of proportions along body where arms are
"right_arm_range" : [0.55, 0.7],
"left_arm_range" : [.34, .462],
}
def __init__(self, mode = "plain", **kwargs):
digest_config(self, kwargs)
self.parts_named = False
try:
svg_file = os.path.join(
PI_CREATURE_DIR,
"%s_%s.svg"%(self.file_name_prefix, mode)
)
SVGMobject.__init__(self, file_name = svg_file, **kwargs)
except:
warnings.warn("No %s design with mode %s"%(self.file_name_prefix, mode))
svg_file = os.path.join(
FILE_DIR,
"PiCreatures_plain.svg",
)
SVGMobject.__init__(self, file_name = svg_file, **kwargs)
if self.flip_at_start:
self.flip()
if self.start_corner is not None:
self.to_corner(self.start_corner)
def name_parts(self):
self.mouth = self.submobjects[MOUTH_INDEX]
self.body = self.submobjects[BODY_INDEX]
self.pupils = VGroup(*[
self.submobjects[LEFT_PUPIL_INDEX],
self.submobjects[RIGHT_PUPIL_INDEX]
])
self.eyes = VGroup(*[
self.submobjects[LEFT_EYE_INDEX],
self.submobjects[RIGHT_EYE_INDEX]
])
self.eye_parts = VGroup(self.eyes, self.pupils)
self.parts_named = True
def init_colors(self):
SVGMobject.init_colors(self)
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 copy(self):
copy_mobject = SVGMobject.copy(self)
copy_mobject.name_parts()
return copy_mobject
def set_color(self, color):
self.body.set_fill(color)
return self
def change_mode(self, mode):
new_self = self.__class__(
mode = mode,
color = self.color
)
new_self.scale_to_fit_height(self.get_height())
if self.is_flipped() ^ new_self.is_flipped():
new_self.flip()
new_self.shift(self.eyes.get_center() - new_self.eyes.get_center())
if hasattr(self, "purposeful_looking_direction"):
new_self.look(self.purposeful_looking_direction)
Transform(self, new_self).update(1)
return self
def look(self, direction):
norm = np.linalg.norm(direction)
if norm == 0:
return
direction /= norm
self.purposeful_looking_direction = direction
for pupil, eye in zip(self.pupils.split(), self.eyes.split()):
pupil_radius = pupil.get_width()/2.
eye_radius = eye.get_width()/2.
pupil.move_to(eye)
if direction[1] < 0:
pupil.shift(pupil_radius*DOWN/3)
pupil.shift(direction*(eye_radius-pupil_radius))
bottom_diff = eye.get_bottom()[1] - pupil.get_bottom()[1]
if bottom_diff > 0:
pupil.shift(bottom_diff*UP)
#TODO, how to handle looking up...
# top_diff = eye.get_top()[1]-pupil.get_top()[1]
# if top_diff < 0:
# pupil.shift(top_diff*UP)
return self
def look_at(self, point_or_mobject):
if isinstance(point_or_mobject, Mobject):
point = point_or_mobject.get_center()
else:
point = point_or_mobject
self.look(point - self.eyes.get_center())
return self
def change(self, new_mode, look_at_arg = None):
self.change_mode(new_mode)
if look_at_arg is not None:
self.look_at(look_at_arg)
return self
def get_looking_direction(self):
return np.sign(np.round(
self.pupils.get_center() - self.eyes.get_center(),
decimals = 2
))
def is_flipped(self):
return self.eyes.submobjects[0].get_center()[0] > \
self.eyes.submobjects[1].get_center()[0]
def blink(self):
eye_parts = self.eye_parts
eye_bottom_y = eye_parts.get_bottom()[1]
eye_parts.apply_function(
lambda p : [p[0], eye_bottom_y, p[2]]
)
return self
def to_corner(self, vect = None, **kwargs):
if vect is not None:
SVGMobject.to_corner(self, vect, **kwargs)
else:
self.scale(self.corner_scale_factor)
self.to_corner(DOWN+LEFT, **kwargs)
return self
def get_bubble(self, *content, **kwargs):
bubble_class = kwargs.get("bubble_class", ThoughtBubble)
bubble = bubble_class(**kwargs)
if len(content) > 0:
if isinstance(content[0], str):
content_mob = TextMobject(*content)
else:
content_mob = content[0]
bubble.add_content(content_mob)
if "height" not in kwargs and "width" not in kwargs:
bubble.resize_to_content()
bubble.pin_to(self)
self.bubble = bubble
return bubble
def make_eye_contact(self, pi_creature):
self.look_at(pi_creature.eyes)
pi_creature.look_at(self.eyes)
return self
def shrug(self):
self.change_mode("shruggie")
top_mouth_point, bottom_mouth_point = [
self.mouth.points[np.argmax(self.mouth.points[:,1])],
self.mouth.points[np.argmin(self.mouth.points[:,1])]
]
self.look(top_mouth_point - bottom_mouth_point)
return self
def get_arm_copies(self):
body = self.body
return VGroup(*[
body.copy().pointwise_become_partial(body, *alpha_range)
for alpha_range in self.right_arm_range, self.left_arm_range
])
def get_all_pi_creature_modes():
result = []
prefix = "%s_"%PiCreature.CONFIG["file_name_prefix"]
suffix = ".svg"
for file in os.listdir(PI_CREATURE_DIR):
if file.startswith(prefix) and file.endswith(suffix):
result.append(
file[len(prefix):-len(suffix)]
)
return result
class Randolph(PiCreature):
pass #Nothing more than an alternative name
class Mortimer(PiCreature):
CONFIG = {
"color" : GREY_BROWN,
"flip_at_start" : True,
}
class Mathematician(PiCreature):
CONFIG = {
"color" : GREY,
}
class BabyPiCreature(PiCreature):
CONFIG = {
"scale_factor" : 0.5,
"eye_scale_factor" : 1.2,
"pupil_scale_factor" : 1.3
}
def __init__(self, *args, **kwargs):
PiCreature.__init__(self, *args, **kwargs)
self.scale(self.scale_factor)
self.shift(LEFT)
self.to_edge(DOWN, buff = LARGE_BUFF)
eyes = VGroup(self.eyes, self.pupils)
eyes_bottom = eyes.get_bottom()
eyes.scale(self.eye_scale_factor)
eyes.move_to(eyes_bottom, aligned_edge = DOWN)
looking_direction = self.get_looking_direction()
for pupil in self.pupils:
pupil.scale_in_place(self.pupil_scale_factor)
self.look(looking_direction)
class TauCreature(PiCreature):
CONFIG = {
"file_name_prefix" : "TauCreatures"
}
class ThreeLeggedPiCreature(PiCreature):
CONFIG = {
"file_name_prefix" : "ThreeLeggedPiCreatures"
}
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 Eyes(VMobject):
CONFIG = {
"height" : 0.3,
"thing_looked_at" : None,
"mode" : "plain",
}
def __init__(self, mobject, **kwargs):
VMobject.__init__(self, **kwargs)
self.mobject = mobject
self.submobjects = self.get_eyes().submobjects
def get_eyes(self, mode = None, thing_to_look_at = None):
mode = mode or self.mode
if thing_to_look_at is None:
thing_to_look_at = self.thing_looked_at
pi = Randolph(mode = mode)
eyes = VGroup(pi.eyes, pi.pupils)
pi.scale(self.height/eyes.get_height())
if self.submobjects:
eyes.move_to(self, DOWN)
else:
eyes.move_to(self.mobject.get_top(), DOWN)
if thing_to_look_at is not None:
pi.look_at(thing_to_look_at)
return eyes
def change_mode_anim(self, mode, **kwargs):
self.mode = mode
return Transform(self, self.get_eyes(mode = mode), **kwargs)
def look_at_anim(self, point_or_mobject, **kwargs):
self.thing_looked_at = point_or_mobject
return Transform(
self, self.get_eyes(thing_to_look_at = point_or_mobject),
**kwargs
)
def blink_anim(self, **kwargs):
target = self.copy()
bottom_y = self.get_bottom()[1]
for submob in target:
submob.apply_function(
lambda p : [p[0], bottom_y, p[2]]
)
if "rate_func" not in kwargs:
kwargs["rate_func"] = squish_rate_func(there_and_back)
return Transform(self, target, **kwargs)
#######################
class PiCreatureBubbleIntroduction(AnimationGroup):
CONFIG = {
"target_mode" : "speaking",
"bubble_class" : SpeechBubble,
"change_mode_kwargs" : {},
"bubble_creation_class" : ShowCreation,
"bubble_creation_kwargs" : {},
"bubble_kwargs" : {},
"content_introduction_class" : Write,
"content_introduction_kwargs" : {},
"look_at_arg" : None,
}
def __init__(self, pi_creature, *content, **kwargs):
digest_config(self, kwargs)
bubble = pi_creature.get_bubble(
*content,
bubble_class = self.bubble_class,
**self.bubble_kwargs
)
Group(bubble, bubble.content).shift_onto_screen()
pi_creature.generate_target()
pi_creature.target.change_mode(self.target_mode)
if self.look_at_arg is not None:
pi_creature.target.look_at(self.look_at_arg)
change_mode = MoveToTarget(pi_creature, **self.change_mode_kwargs)
bubble_creation = self.bubble_creation_class(
bubble, **self.bubble_creation_kwargs
)
content_introduction = self.content_introduction_class(
bubble.content, **self.content_introduction_kwargs
)
AnimationGroup.__init__(
self, change_mode, bubble_creation, content_introduction,
**kwargs
)
class PiCreatureSays(PiCreatureBubbleIntroduction):
CONFIG = {
"target_mode" : "speaking",
"bubble_class" : SpeechBubble,
}
class RemovePiCreatureBubble(AnimationGroup):
CONFIG = {
"target_mode" : "plain",
"look_at_arg" : None,
"remover" : True,
}
def __init__(self, pi_creature, **kwargs):
assert hasattr(pi_creature, "bubble")
digest_config(self, kwargs, locals())
pi_creature.generate_target()
pi_creature.target.change_mode(self.target_mode)
if self.look_at_arg is not None:
pi_creature.target.look_at(self.look_at_arg)
AnimationGroup.__init__(
self,
MoveToTarget(pi_creature),
FadeOut(pi_creature.bubble),
FadeOut(pi_creature.bubble.content),
)
def clean_up(self, surrounding_scene = None):
AnimationGroup.clean_up(self, surrounding_scene)
self.pi_creature.bubble = None
if surrounding_scene is not None:
surrounding_scene.add(self.pi_creature)
###########
class PiCreatureScene(Scene):
CONFIG = {
"total_wait_time" : 0,
"seconds_to_blink" : 3,
"pi_creatures_start_on_screen" : True,
"default_pi_creature_kwargs" : {
"color" : GREY_BROWN,
"flip_at_start" : True,
},
"default_pi_creature_start_corner" : DOWN+LEFT,
}
def setup(self):
self.pi_creatures = self.create_pi_creatures()
self.pi_creature = self.get_primary_pi_creature()
if self.pi_creatures_start_on_screen:
self.add(*self.pi_creatures)
def create_pi_creatures(self):
"""
Likely updated for subclasses
"""
return VGroup(self.create_pi_creature())
def create_pi_creature(self):
pi_creature = PiCreature(**self.default_pi_creature_kwargs)
pi_creature.to_corner(self.default_pi_creature_start_corner)
return pi_creature
def get_pi_creatures(self):
return self.pi_creatures
def get_primary_pi_creature(self):
return self.pi_creatures[0]
def any_pi_creatures_on_screen(self):
mobjects = self.get_mobjects()
return any([pi in mobjects for pi in self.get_pi_creatures()])
def get_on_screen_pi_creatures(self):
mobjects = self.get_mobjects()
return VGroup(*filter(
lambda pi : pi in mobjects,
self.get_pi_creatures()
))
def introduce_bubble(self, *args, **kwargs):
if isinstance(args[0], PiCreature):
pi_creature = args[0]
content = args[1:]
else:
pi_creature = self.get_primary_pi_creature()
content = args
bubble_class = kwargs.pop("bubble_class", SpeechBubble)
target_mode = kwargs.pop(
"target_mode",
"thinking" if bubble_class is ThoughtBubble else "speaking"
)
bubble_kwargs = kwargs.pop("bubble_kwargs", {})
bubble_removal_kwargs = kwargs.pop("bubble_removal_kwargs", {})
added_anims = kwargs.pop("added_anims", [])
anims = []
on_screen_mobjects = self.camera.extract_mobject_family_members(
self.get_mobjects()
)
def has_bubble(pi):
return hasattr(pi, "bubble") and \
pi.bubble is not None and \
pi.bubble in on_screen_mobjects
pi_creatures_with_bubbles = filter(has_bubble, self.get_pi_creatures())
if pi_creature in pi_creatures_with_bubbles:
pi_creatures_with_bubbles.remove(pi_creature)
old_bubble = pi_creature.bubble
bubble = pi_creature.get_bubble(
*content,
bubble_class = bubble_class,
**bubble_kwargs
)
anims += [
ReplacementTransform(old_bubble, bubble),
ReplacementTransform(old_bubble.content, bubble.content),
pi_creature.change_mode, target_mode
]
else:
anims.append(PiCreatureBubbleIntroduction(
pi_creature,
*content,
bubble_class = bubble_class,
bubble_kwargs = bubble_kwargs,
target_mode = target_mode,
**kwargs
))
anims += [
RemovePiCreatureBubble(pi, **bubble_removal_kwargs)
for pi in pi_creatures_with_bubbles
]
anims += added_anims
self.play(*anims, **kwargs)
def pi_creature_says(self, *args, **kwargs):
self.introduce_bubble(
*args,
bubble_class = SpeechBubble,
**kwargs
)
def pi_creature_thinks(self, *args, **kwargs):
self.introduce_bubble(
*args,
bubble_class = ThoughtBubble,
**kwargs
)
def say(self, *content, **kwargs):
self.pi_creature_says(self.get_primary_pi_creature(), *content, **kwargs)
def think(self, *content, **kwargs):
self.pi_creature_thinks(self.get_primary_pi_creature(), *content, **kwargs)
def compile_play_args_to_animation_list(self, *args):
"""
Add animations so that all pi creatures look at the
first mobject being animated with each .play call
"""
animations = Scene.compile_play_args_to_animation_list(self, *args)
if not self.any_pi_creatures_on_screen():
return animations
non_pi_creature_anims = filter(
lambda anim : anim.mobject not in self.get_pi_creatures(),
animations
)
if len(non_pi_creature_anims) == 0:
return animations
first_anim = non_pi_creature_anims[0]
#Look at ending state
first_anim.update(1)
point_of_interest = first_anim.mobject.get_center()
first_anim.update(0)
for pi_creature in self.get_pi_creatures():
if pi_creature not in self.get_mobjects():
continue
if pi_creature in first_anim.mobject.submobject_family():
continue
anims_with_pi_creature = filter(
lambda anim : pi_creature in anim.mobject.submobject_family(),
animations
)
for anim in anims_with_pi_creature:
if isinstance(anim, Transform):
index = anim.mobject.submobject_family().index(pi_creature)
target_family = anim.target_mobject.submobject_family()
target = target_family[index]
if isinstance(target, PiCreature):
target.look_at(point_of_interest)
if not anims_with_pi_creature:
animations.append(
ApplyMethod(pi_creature.look_at, point_of_interest)
)
return animations
def blink(self):
self.play(Blink(random.choice(self.get_on_screen_pi_creatures())))
def joint_blink(self, pi_creatures = None, shuffle = True, **kwargs):
if pi_creatures is None:
pi_creatures = self.get_on_screen_pi_creatures()
creatures_list = list(pi_creatures)
if shuffle:
random.shuffle(creatures_list)
def get_rate_func(pi):
index = creatures_list.index(pi)
proportion = float(index)/len(creatures_list)
start_time = 0.8*proportion
return squish_rate_func(
there_and_back,
start_time, start_time + 0.2
)
self.play(*[
Blink(pi, rate_func = get_rate_func(pi), **kwargs)
for pi in creatures_list
])
return self
def wait(self, time = 1, blink = True):
while time >= 1:
time_to_blink = self.total_wait_time%self.seconds_to_blink == 0
if blink and self.any_pi_creatures_on_screen() and time_to_blink:
self.blink()
self.num_plays -= 1 #This shouldn't count as an animation
else:
self.non_blink_wait()
time -= 1
self.total_wait_time += 1
if time > 0:
self.non_blink_wait(time)
return self
def non_blink_wait(self, time = 1):
Scene.wait(self, time)
return self
def change_mode(self, mode):
self.play(self.get_primary_pi_creature().change_mode, mode)
def look_at(self, thing_to_look_at, pi_creatures = None):
if pi_creatures is None:
pi_creatures = self.get_pi_creatures()
self.play(*it.chain(*[
[pi.look_at, thing_to_look_at]
for pi in pi_creatures
]))
class TeacherStudentsScene(PiCreatureScene):
CONFIG = {
"student_colors" : [BLUE_D, BLUE_E, BLUE_C],
"student_scale_factor" : 0.8,
"seconds_to_blink" : 2,
"screen_height" : 3,
}
def setup(self):
PiCreatureScene.setup(self)
self.screen = ScreenRectangle(height = self.screen_height)
self.screen.to_corner(UP+LEFT)
self.hold_up_spot = self.teacher.get_corner(UP+LEFT) + MED_LARGE_BUFF*UP
def create_pi_creatures(self):
self.teacher = Mortimer()
self.teacher.to_corner(DOWN + RIGHT)
self.teacher.look(DOWN+LEFT)
self.students = VGroup(*[
Randolph(color = c)
for c in self.student_colors
])
self.students.arrange_submobjects(RIGHT)
self.students.scale(self.student_scale_factor)
self.students.to_corner(DOWN+LEFT)
self.teacher.look_at(self.students[-1].eyes)
for student in self.students:
student.look_at(self.teacher.eyes)
return [self.teacher] + list(self.students)
def get_teacher(self):
return self.teacher
def get_students(self):
return self.students
def teacher_says(self, *content, **kwargs):
return self.pi_creature_says(
self.get_teacher(), *content, **kwargs
)
def student_says(self, *content, **kwargs):
if "target_mode" not in kwargs:
target_mode = random.choice([
"raise_right_hand",
"raise_left_hand",
])
kwargs["target_mode"] = target_mode
student = self.get_students()[kwargs.get("student_index", 1)]
return self.pi_creature_says(
student, *content, **kwargs
)
def teacher_thinks(self, *content, **kwargs):
return self.pi_creature_thinks(
self.get_teacher(), *content, **kwargs
)
def student_thinks(self, *content, **kwargs):
student = self.get_students()[kwargs.get("student_index", 1)]
return self.pi_creature_thinks(student, *content, **kwargs)
def change_all_student_modes(self, mode, **kwargs):
self.change_student_modes(*[mode]*len(self.students), **kwargs)
def change_student_modes(self, *modes, **kwargs):
added_anims = kwargs.pop("added_anims", [])
self.play(
self.get_student_changes(*modes, **kwargs),
*added_anims
)
def get_student_changes(self, *modes, **kwargs):
pairs = zip(self.get_students(), modes)
pairs = [(s, m) for s, m in pairs if m is not None]
start = VGroup(*[s for s, m in pairs])
target = VGroup(*[s.copy().change_mode(m) for s, m in pairs])
if "look_at_arg" in kwargs:
for pi in target:
pi.look_at(kwargs["look_at_arg"])
submobject_mode = kwargs.get("submobject_mode", "lagged_start")
return Transform(
start, target,
submobject_mode = submobject_mode,
run_time = 2
)
def zoom_in_on_thought_bubble(self, bubble = None, radius = FRAME_Y_RADIUS+FRAME_X_RADIUS):
if bubble is None:
for pi in self.get_pi_creatures():
if hasattr(pi, "bubble") and isinstance(pi.bubble, ThoughtBubble):
bubble = pi.bubble
break
if bubble is None:
raise Exception("No pi creatures have a thought bubble")
vect = -bubble.get_bubble_center()
def func(point):
centered = point+vect
return radius*centered/np.linalg.norm(centered)
self.play(*[
ApplyPointwiseFunction(func, mob)
for mob in self.get_mobjects()
])
def teacher_holds_up(self, mobject, target_mode = "raise_right_hand", **kwargs):
mobject.move_to(self.hold_up_spot, DOWN)
mobject.shift_onto_screen()
mobject_copy = mobject.copy()
mobject_copy.shift(DOWN)
mobject_copy.fade(1)
self.play(
ReplacementTransform(mobject_copy, mobject),
self.teacher.change, target_mode,
)

View file

@ -20,7 +20,6 @@ from topics.geometry import Rectangle
from topics.geometry import Square
from topics.objects import PatreonLogo
class OpeningQuote(Scene):
CONFIG = {
"quote" : [],

View file

@ -18,7 +18,6 @@ from utils.paths import path_along_arc
from utils.space_ops import R3_to_complex
from utils.space_ops import complex_to_R3
class ComplexTransformationScene(Scene):
CONFIG = {
"plane_config" : {},

View file

@ -1,4 +1,3 @@
# from mobject.mobject import Mobject, Point, Mobject1D
from animation.creation import ShowCreation
from animation.transform import Transform
from characters import PiCreature
@ -72,7 +71,6 @@ def fractalification_iteration(vmobject, dimension = 1.05, num_inserted_anchors_
]
return vmobject
class SelfSimilarFractal(VMobject):
CONFIG = {
"order" : 5,
@ -117,7 +115,6 @@ class SelfSimilarFractal(VMobject):
def arrange_subparts(self, *subparts):
raise Exception("Not implemented")
class Sierpinski(SelfSimilarFractal):
def get_seed_shape(self):
return Polygon(
@ -129,7 +126,6 @@ class Sierpinski(SelfSimilarFractal):
tri1.move_to(tri2.get_corner(DOWN+LEFT), UP)
tri3.move_to(tri2.get_corner(DOWN+RIGHT), UP)
class DiamondFractal(SelfSimilarFractal):
CONFIG = {
"num_subparts" : 4,
@ -145,7 +141,6 @@ class DiamondFractal(SelfSimilarFractal):
part.next_to(ORIGIN, vect, buff = 0)
VGroup(*subparts).rotate(np.pi/4, about_point = ORIGIN)
class PentagonalFractal(SelfSimilarFractal):
CONFIG = {
"num_subparts" : 5,
@ -182,7 +177,6 @@ class PentagonalPiCreatureFractal(PentagonalFractal):
part.rotate(2*np.pi/5, about_point = ORIGIN)
PentagonalFractal.arrange_subparts(self, *subparts)
class PiCreatureFractal(VMobject):
CONFIG = {
"order" : 7,
@ -242,7 +236,6 @@ class PiCreatureFractal(VMobject):
# VMobject.init_colors(self)
# self.set_color_by_gradient(*self.colors)
class WonkyHexagonFractal(SelfSimilarFractal):
CONFIG = {
"num_subparts" : 7
@ -296,7 +289,6 @@ class JaggedCurvePiece(VMobject):
indices = np.linspace(0, len(anchors)-1, n+len(anchors)).astype('int')
self.set_points_as_corners(anchors[indices])
class FractalCurve(VMobject):
CONFIG = {
"radius" : 3,
@ -335,7 +327,6 @@ class FractalCurve(VMobject):
def get_anchor_points(self):
raise Exception("Not implemented")
class LindenmayerCurve(FractalCurve):
CONFIG = {
"axiom" : "A",
@ -376,7 +367,6 @@ class LindenmayerCurve(FractalCurve):
result.append(curr)
return np.array(result) - center_of_mass(result)
class SelfSimilarSpaceFillingCurve(FractalCurve):
CONFIG = {
"offsets" : [],
@ -420,8 +410,6 @@ class SelfSimilarSpaceFillingCurve(FractalCurve):
def generate_grid(self):
raise Exception("Not implemented")
class HilbertCurve(SelfSimilarSpaceFillingCurve):
CONFIG = {
"offsets" : [
@ -436,7 +424,6 @@ class HilbertCurve(SelfSimilarSpaceFillingCurve):
},
}
class HilbertCurve3D(SelfSimilarSpaceFillingCurve):
CONFIG = {
"offsets" : [
@ -472,7 +459,6 @@ class HilbertCurve3D(SelfSimilarSpaceFillingCurve):
copy += offset*self.radius*self.radius_scale_factor
return copy
class PeanoCurve(SelfSimilarSpaceFillingCurve):
CONFIG = {
"colors" : [PURPLE, TEAL],
@ -607,7 +593,6 @@ class KochCurve(KochSnowFlake):
CONFIG = {
"axiom" : "A--"
}
class QuadraticKoch(LindenmayerCurve):
CONFIG = {
@ -622,7 +607,6 @@ class QuadraticKoch(LindenmayerCurve):
"angle" : np.pi/2
}
class QuadraticKochIsland(QuadraticKoch):
CONFIG = {
"axiom" : "A+A+A+A"

View file

@ -108,8 +108,6 @@ class Arc(VMobject):
return self
class ArcBetweenPoints(Arc):
def __init__(self, start_point, end_point, angle = TAU/4, **kwargs):
@ -150,7 +148,6 @@ class CurvedArrow(ArcBetweenPoints):
else:
ArcBetweenPoints.__init__(self, end_point, start_point, angle = -angle, **kwargs)
self.add_tip(at_start = False, at_end = True)
class CurvedDoubleArrow(ArcBetweenPoints):
@ -158,7 +155,6 @@ class CurvedDoubleArrow(ArcBetweenPoints):
ArcBetweenPoints.__init__(self, start_point, end_point, angle = angle, **kwargs)
self.add_tip(at_start = True, at_end = True)
class Circle(Arc):
CONFIG = {
"color" : RED,

View file

@ -30,7 +30,6 @@ class Graph():
def __str__(self):
return self.__class__.__name__
class CubeGraph(Graph):
"""
5 7
@ -67,7 +66,6 @@ class CubeGraph(Graph):
[4, 6, 7, 5],#By convention, last region will be "outside"
]
class SampleGraph(Graph):
"""
4 2 3 8
@ -115,7 +113,6 @@ class SampleGraph(Graph):
(4, 5, 6, 7, 8, 3, 2),
]
class OctohedronGraph(Graph):
"""
3
@ -155,7 +152,6 @@ class OctohedronGraph(Graph):
(3, 4, 5),
]
class CompleteGraph(Graph):
def __init__(self, num_vertices, radius = 3):
self.num_vertices = num_vertices
@ -173,7 +169,6 @@ class CompleteGraph(Graph):
def __str__(self):
return Graph.__str__(self) + str(self.num_vertices)
class GraphScene(Scene):
args_list = [
(CubeGraph(),),

View file

@ -39,7 +39,6 @@ def matrix_to_tex_string(matrix):
]
return prefix + " \\\\ ".join(rows) + suffix
def matrix_to_mobject(matrix):
return TexMobject(matrix_to_tex_string(matrix))
@ -139,8 +138,6 @@ class Matrix(VMobject):
def get_brackets(self):
return self.brackets
class NumericalMatrixMultiplication(Scene):
CONFIG = {
"left_matrix" : [[1, 2], [3, 4]],