From 0978984541bf99c4a1ff3ac8664c9f3580a19ccb Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Sat, 31 Mar 2018 15:11:35 -0700 Subject: [PATCH 01/17] Reorganized animations folder. Warning: While I tried to be systematic, there is a decent chance this will cause import errors somewhere. --- .gitignore | 4 +- active_projects/eop/bayes.py | 2 +- active_projects/eop/bayes_footnote.py | 2 +- active_projects/eop/combinations.py | 2 +- active_projects/eop/independence.py | 2 +- animation/__init__.py | 5 - animation/{compositions.py => composition.py} | 118 +++--- animation/creation.py | 185 ++++++++++ animation/indication.py | 187 ++++++++++ animation/movement.py | 62 ++++ animation/playground.py | 62 ---- animation/rotation.py | 61 ++++ animation/simple_animations.py | 336 ------------------ animation/transform.py | 173 ++------- animation/update.py | 47 +++ big_ol_pile_of_manim_imports.py | 29 +- camera/camera.py | 22 +- continual_animation/__init__.py | 0 .../continual_animation.py | 2 +- extract_scene.py | 11 +- mobject/image_mobject.py | 9 +- mobject/mobject.py | 17 +- mobject/point_cloud_mobject.py | 8 +- mobject/svg_mobject.py | 14 +- mobject/tex_mobject.py | 9 +- mobject/vectorized_mobject.py | 12 +- old_projects/basel/basel.py | 27 +- old_projects/basel/basel2.py | 1 + {topics => old_projects/basel}/light.py | 25 +- old_projects/nn/playground.py | 2 +- scene/__init__.py | 4 +- scene/moving_camera_scene.py | 4 +- scene/reconfigurable_scene.py | 4 +- scene/scene.py | 33 +- scene/scene_from_video.py | 7 +- scene/tk_scene.py | 44 --- scene/zoomed_scene.py | 10 +- stage_scenes.py | 11 +- topics/__init__.py | 11 - topics/arithmetic.py | 6 +- topics/characters.py | 33 +- topics/combinatorics.py | 2 +- topics/common_scenes.py | 22 +- topics/complex_numbers.py | 20 +- topics/counting.py | 16 +- topics/fractals.py | 23 +- topics/geometry.py | 11 +- topics/graph_scene.py | 18 +- topics/graph_theory.py | 1 + topics/matrix.py | 28 +- topics/number_line.py | 8 +- topics/numerals.py | 12 +- topics/objects.py | 35 +- topics/probability.py | 23 +- topics/three_dimensions.py | 17 +- topics/vector_space_scene.py | 39 +- utils/bezier.py | 1 + utils/color.py | 3 +- utils/images.py | 3 +- utils/iterables.py | 2 +- utils/paths.py | 3 +- utils/rate_functions.py | 3 +- utils/simple_functions.py | 2 +- utils/space_ops.py | 4 +- 64 files changed, 999 insertions(+), 900 deletions(-) rename animation/{compositions.py => composition.py} (89%) create mode 100644 animation/creation.py create mode 100644 animation/indication.py create mode 100644 animation/movement.py delete mode 100644 animation/playground.py create mode 100644 animation/rotation.py delete mode 100644 animation/simple_animations.py create mode 100644 animation/update.py create mode 100644 continual_animation/__init__.py rename {animation => continual_animation}/continual_animation.py (98%) rename {topics => old_projects/basel}/light.py (96%) delete mode 100644 scene/tk_scene.py diff --git a/.gitignore b/.gitignore index 3175f56c..87cbc012 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,8 @@ *.pyc .DS_Store homeless.py -ka_playgrounds/ playground.py -special_animations.py -prettiness_hall_of_fame.py +random_scenes/ files/ ben_playground.py ben_cairo_test.py diff --git a/active_projects/eop/bayes.py b/active_projects/eop/bayes.py index 1890d91b..1ad2d9bd 100644 --- a/active_projects/eop/bayes.py +++ b/active_projects/eop/bayes.py @@ -8,7 +8,7 @@ from mobject.vectorized_mobject import * from animation.animation import Animation from animation.transform import * from animation.simple_animations import * -from animation.compositions import * +from animation.composition import * from animation.playground import * from topics.geometry import * from topics.characters import * diff --git a/active_projects/eop/bayes_footnote.py b/active_projects/eop/bayes_footnote.py index e3719216..38bacda7 100644 --- a/active_projects/eop/bayes_footnote.py +++ b/active_projects/eop/bayes_footnote.py @@ -8,7 +8,7 @@ from mobject.vectorized_mobject import * from animation.animation import Animation from animation.transform import * from animation.simple_animations import * -from animation.compositions import * +from animation.composition import * from animation.playground import * from topics.geometry import * from topics.characters import * diff --git a/active_projects/eop/combinations.py b/active_projects/eop/combinations.py index ec7d60c1..906fa96f 100644 --- a/active_projects/eop/combinations.py +++ b/active_projects/eop/combinations.py @@ -8,7 +8,7 @@ from mobject.vectorized_mobject import * from animation.animation import Animation from animation.transform import * from animation.simple_animations import * -from animation.compositions import * +from animation.composition import * from animation.playground import * from animation.continual_animation import * from topics.geometry import * diff --git a/active_projects/eop/independence.py b/active_projects/eop/independence.py index 208b4a52..d31b65b3 100644 --- a/active_projects/eop/independence.py +++ b/active_projects/eop/independence.py @@ -8,7 +8,7 @@ from mobject.vectorized_mobject import * from animation.animation import Animation from animation.transform import * from animation.simple_animations import * -from animation.compositions import * +from animation.composition import * from animation.playground import * from topics.geometry import * from topics.characters import * diff --git a/animation/__init__.py b/animation/__init__.py index de0b98d3..e69de29b 100644 --- a/animation/__init__.py +++ b/animation/__init__.py @@ -1,5 +0,0 @@ -__all__ = [ - "animation", - "simple_animations", - "transform" -] \ No newline at end of file diff --git a/animation/compositions.py b/animation/composition.py similarity index 89% rename from animation/compositions.py rename to animation/composition.py index 8b26b566..f05aeb64 100644 --- a/animation/compositions.py +++ b/animation/composition.py @@ -1,54 +1,27 @@ -import numpy as np +from __future__ import absolute_import + import itertools as it +import numpy as np from constants import * import warnings -from mobject.mobject import Mobject, Group -from mobject.vectorized_mobject import VMobject -from mobject.tex_mobject import TextMobject -from .animation import Animation -from transform import Transform + +from animation.animation import Animation +from mobject.mobject import Group +from mobject.mobject import Mobject from utils.bezier import inverse_interpolate from utils.config_ops import digest_config from utils.rate_functions import squish_rate_func -class LaggedStart(Animation): +class EmptyAnimation(Animation): CONFIG = { - "run_time" : 2, - "lag_ratio" : 0.5, + "run_time" : 0, + "empty" : True } - def __init__(self, AnimationClass, mobject, arg_creator = None, **kwargs): - digest_config(self, kwargs) - for key in "rate_func", "run_time", "lag_ratio": - if key in kwargs: - kwargs.pop(key) - if arg_creator is None: - arg_creator = lambda mobject : (mobject,) - self.subanimations = [ - AnimationClass( - *arg_creator(submob), - run_time = self.run_time, - rate_func = squish_rate_func( - self.rate_func, beta, beta + self.lag_ratio - ), - **kwargs - ) - for submob, beta in zip( - mobject, - np.linspace(0, 1-self.lag_ratio, len(mobject)) - ) - ] - Animation.__init__(self, mobject, **kwargs) - def update(self, alpha): - for anim in self.subanimations: - anim.update(alpha) - return self - - def clean_up(self, *args, **kwargs): - for anim in self.subanimations: - anim.clean_up(*args, **kwargs) + def __init__(self, *args, **kwargs): + return Animation.__init__(self, Group(), *args, **kwargs) class Succession(Animation): CONFIG = { @@ -231,11 +204,64 @@ class AnimationGroup(Animation): for anim in self.sub_anims: anim.update_config(**kwargs) -class EmptyAnimation(Animation): - CONFIG = { - "run_time" : 0, - "empty" : True - } +# Variants on mappin an animation over submobjectsg + +class LaggedStart(Animation): + CONFIG = { + "run_time" : 2, + "lag_ratio" : 0.5, + } + def __init__(self, AnimationClass, mobject, arg_creator = None, **kwargs): + digest_config(self, kwargs) + for key in "rate_func", "run_time", "lag_ratio": + if key in kwargs: + kwargs.pop(key) + if arg_creator is None: + arg_creator = lambda mobject : (mobject,) + self.subanimations = [ + AnimationClass( + *arg_creator(submob), + run_time = self.run_time, + rate_func = squish_rate_func( + self.rate_func, beta, beta + self.lag_ratio + ), + **kwargs + ) + for submob, beta in zip( + mobject, + np.linspace(0, 1-self.lag_ratio, len(mobject)) + ) + ] + Animation.__init__(self, mobject, **kwargs) + + def update(self, alpha): + for anim in self.subanimations: + anim.update(alpha) + return self + + def clean_up(self, *args, **kwargs): + for anim in self.subanimations: + anim.clean_up(*args, **kwargs) + +class ApplyToCenters(Animation): + def __init__(self, AnimationClass, mobjects, **kwargs): + full_kwargs = AnimationClass.CONFIG + full_kwargs.update(kwargs) + full_kwargs["mobject"] = Mobject(*[ + mob.get_point_mobject() + for mob in mobjects + ]) + self.centers_container = AnimationClass(**full_kwargs) + full_kwargs.pop("mobject") + Animation.__init__(self, Mobject(*mobjects), **full_kwargs) + self.name = str(self) + AnimationClass.__name__ + + def update_mobject(self, alpha): + self.centers_container.update_mobject(alpha) + center_mobs = self.centers_container.mobject.split() + mobjects = self.mobject.split() + for center_mob, mobject in zip(center_mobs, mobjects): + mobject.shift( + center_mob.get_center()-mobject.get_center() + ) - def __init__(self, *args, **kwargs): - return Animation.__init__(self, Group(), *args, **kwargs) diff --git a/animation/creation.py b/animation/creation.py new file mode 100644 index 00000000..8ce90f0d --- /dev/null +++ b/animation/creation.py @@ -0,0 +1,185 @@ +from __future__ import absolute_import + +import numpy as np + +from constants import * + +from animation.animation import Animation +from mobject.tex_mobject import TextMobject +from mobject.vectorized_mobject import VMobject +from mobject.vectorized_mobject import VectorizedPoint +from animation.transform import Transform +from utils.bezier import interpolate +from utils.config_ops import digest_config +from utils.paths import counterclockwise_path +from utils.rate_functions import double_smooth +from utils.rate_functions import smooth + +#Drawing + +class ShowPartial(Animation): + def update_submobject(self, submobject, starting_submobject, alpha): + submobject.pointwise_become_partial( + starting_submobject, *self.get_bounds(alpha) + ) + + def get_bounds(self, alpha): + raise Exception("Not Implemented") + +class ShowCreation(ShowPartial): + CONFIG = { + "submobject_mode" : "one_at_a_time", + } + def get_bounds(self, alpha): + return (0, alpha) + +class Uncreate(ShowCreation): + CONFIG = { + "rate_func" : lambda t : smooth(1-t), + "remover" : True + } + +class Write(ShowCreation): + CONFIG = { + "rate_func" : None, + "submobject_mode" : "lagged_start", + } + def __init__(self, mob_or_text, **kwargs): + digest_config(self, kwargs) + if isinstance(mob_or_text, str): + mobject = TextMobject(mob_or_text) + else: + mobject = mob_or_text + if "run_time" not in kwargs: + self.establish_run_time(mobject) + if "lag_factor" not in kwargs: + if len(mobject.family_members_with_points()) < 4: + min_lag_factor = 1 + else: + min_lag_factor = 2 + self.lag_factor = max(self.run_time - 1, min_lag_factor) + ShowCreation.__init__(self, mobject, **kwargs) + + def establish_run_time(self, mobject): + num_subs = len(mobject.family_members_with_points()) + if num_subs < 15: + self.run_time = 1 + else: + self.run_time = 2 + +class DrawBorderThenFill(Animation): + CONFIG = { + "run_time" : 2, + "stroke_width" : 2, + "stroke_color" : None, + "rate_func" : double_smooth, + } + def __init__(self, vmobject, **kwargs): + if not isinstance(vmobject, VMobject): + raise Exception("DrawBorderThenFill only works for VMobjects") + self.reached_halfway_point_before = False + Animation.__init__(self, vmobject, **kwargs) + + def update_submobject(self, submobject, starting_submobject, alpha): + submobject.pointwise_become_partial( + starting_submobject, 0, min(2*alpha, 1) + ) + if alpha < 0.5: + if self.stroke_color: + color = self.stroke_color + elif starting_submobject.stroke_width > 0: + color = starting_submobject.get_stroke_color() + else: + color = starting_submobject.get_color() + submobject.set_stroke(color, width = self.stroke_width) + submobject.set_fill(opacity = 0) + else: + if not self.reached_halfway_point_before: + self.reached_halfway_point_before = True + submobject.points = np.array(starting_submobject.points) + width, opacity = [ + interpolate(start, end, 2*alpha - 1) + for start, end in [ + (self.stroke_width, starting_submobject.get_stroke_width()), + (0, starting_submobject.get_fill_opacity()) + ] + ] + submobject.set_stroke(width = width) + submobject.set_fill(opacity = opacity) + +#Fading + +class FadeOut(Transform): + CONFIG = { + "remover" : True, + } + def __init__(self, mobject, **kwargs): + target = mobject.copy() + target.fade(1) + Transform.__init__(self, mobject, target, **kwargs) + + def clean_up(self, surrounding_scene = None): + Transform.clean_up(self, surrounding_scene) + self.update(0) + +class FadeIn(Transform): + def __init__(self, mobject, **kwargs): + target = mobject.copy() + Transform.__init__(self, mobject, target, **kwargs) + self.starting_mobject.fade(1) + if isinstance(self.starting_mobject, VMobject): + self.starting_mobject.set_stroke(width = 0) + self.starting_mobject.set_fill(opacity = 0) + +class FadeInAndShiftFromDirection(Transform): + CONFIG = { + "direction" : DOWN, + } + def __init__(self, mobject, **kwargs): + digest_config(self, kwargs) + target = mobject.copy() + mobject.shift(self.direction) + mobject.fade(1) + Transform.__init__(self, mobject, target, **kwargs) + +class FadeInFromDown(FadeInAndShiftFromDirection): + """ + Essential a more convenient form of FadeInAndShiftFromDirection + """ + CONFIG = { + "direction" : DOWN, + } + +#Growing +class GrowFromPoint(Transform): + CONFIG = { + "point_color" : None, + } + def __init__(self, mobject, point, **kwargs): + digest_config(self, kwargs) + target = mobject.copy() + point_mob = VectorizedPoint(point) + if self.point_color: + point_mob.set_color(self.point_color) + mobject.replace(point_mob) + mobject.set_color(point_mob.get_color()) + Transform.__init__(self, mobject, target, **kwargs) + +class GrowFromCenter(GrowFromPoint): + def __init__(self, mobject, **kwargs): + GrowFromPoint.__init__(self, mobject, mobject.get_center(), **kwargs) + +class GrowArrow(GrowFromPoint): + def __init__(self, arrow, **kwargs): + GrowFromPoint.__init__(self, arrow, arrow.get_start(), **kwargs) + +class SpinInFromNothing(GrowFromCenter): + CONFIG = { + "path_func" : counterclockwise_path() + } + +class ShrinkToCenter(Transform): + def __init__(self, mobject, **kwargs): + Transform.__init__( + self, mobject, mobject.get_point_mobject(), **kwargs + ) diff --git a/animation/indication.py b/animation/indication.py new file mode 100644 index 00000000..67531863 --- /dev/null +++ b/animation/indication.py @@ -0,0 +1,187 @@ +from __future__ import absolute_import + +import numpy as np + +from constants import * + +from animation.animation import Animation +from animation.movement import Homotopy +from animation.creation import ShowPartial +from animation.transform import Transform +from mobject.mobject import Group +from mobject.mobject import Mobject +from mobject.vectorized_mobject import VMobject +from topics.geometry import Circle +from topics.geometry import Dot +from utils.config_ops import digest_config +from utils.rate_functions import squish_rate_func +from utils.rate_functions import there_and_back + + +class FocusOn(Transform): + CONFIG = { + "opacity" : 0.2, + "color" : GREY, + "run_time" : 2, + "remover" : True, + } + def __init__(self, mobject_or_point, **kwargs): + digest_config(self, kwargs) + big_dot = Dot( + radius = FRAME_X_RADIUS+FRAME_Y_RADIUS, + stroke_width = 0, + fill_color = self.color, + fill_opacity = 0, + ) + little_dot = Dot(radius = 0) + little_dot.set_fill(self.color, opacity = self.opacity) + little_dot.move_to(mobject_or_point) + + Transform.__init__(self, big_dot, little_dot, **kwargs) + +class Indicate(Transform): + CONFIG = { + "rate_func" : there_and_back, + "scale_factor" : 1.2, + "color" : YELLOW, + } + def __init__(self, mobject, **kwargs): + digest_config(self, kwargs) + target = mobject.copy() + target.scale_in_place(self.scale_factor) + target.set_color(self.color) + Transform.__init__(self, mobject, target, **kwargs) + +class CircleIndicate(Indicate): + CONFIG = { + "rate_func" : squish_rate_func(there_and_back, 0, 0.8), + "remover" : True + } + def __init__(self, mobject, **kwargs): + digest_config(self, kwargs) + circle = Circle(color = self.color, **kwargs) + circle.surround(mobject) + Indicate.__init__(self, circle, **kwargs) + +class ShowPassingFlash(ShowPartial): + CONFIG = { + "time_width" : 0.1, + "remover" : True, + } + def get_bounds(self, alpha): + alpha *= (1+self.time_width) + alpha -= self.time_width/2.0 + lower = max(0, alpha - self.time_width/2.0) + upper = min(1, alpha + self.time_width/2.0) + return (lower, upper) + + def clean_up(self, *args, **kwargs): + ShowPartial.clean_up(self, *args, **kwargs) + for submob, start_submob in self.get_all_families_zipped(): + submob.pointwise_become_partial(start_submob, 0, 1) + +class ShowCreationThenDestruction(ShowPassingFlash): + CONFIG = { + "time_width" : 2.0, + "run_time" : 1, + } + +class ApplyWave(Homotopy): + CONFIG = { + "direction" : DOWN, + "amplitude" : 0.2, + "run_time" : 1, + "apply_function_kwargs" : { + "maintain_smoothness" : False, + }, + } + def __init__(self, mobject, **kwargs): + digest_config(self, kwargs, locals()) + left_x = mobject.get_left()[0] + right_x = mobject.get_right()[0] + vect = self.amplitude*self.direction + def homotopy(x, y, z, t): + start_point = np.array([x, y, z]) + alpha = (x-left_x)/(right_x-left_x) + power = np.exp(2*(alpha-0.5)) + nudge = there_and_back(t**power) + return np.array([x, y, z]) + nudge*vect + Homotopy.__init__(self, homotopy, mobject, **kwargs) + +class WiggleOutThenIn(Animation): + CONFIG = { + "scale_value" : 1.1, + "rotation_angle" : 0.01*TAU, + "n_wiggles" : 6, + "run_time" : 2, + "scale_about_point" : None, + "rotate_about_point" : None, + } + def __init__(self, mobject, **kwargs): + digest_config(self, kwargs) + if self.scale_about_point is None: + self.scale_about_point = mobject.get_center() + if self.rotate_about_point is None: + self.rotate_about_point = mobject.get_center() + Animation.__init__(self, mobject, **kwargs) + + def update_submobject(self, submobject, starting_sumobject, alpha): + submobject.points[:,:] = starting_sumobject.points + submobject.scale( + interpolate(1, self.scale_value, there_and_back(alpha)), + about_point = self.scale_about_point + ) + submobject.rotate( + wiggle(alpha, self.n_wiggles)*self.rotation_angle, + about_point = self.rotate_about_point + ) + +class Vibrate(Animation): + CONFIG = { + "spatial_period" : 6, + "temporal_period" : 1, + "overtones" : 4, + "amplitude" : 0.5, + "radius" : FRAME_X_RADIUS/2, + "run_time" : 3.0, + "rate_func" : None + } + def __init__(self, mobject = None, **kwargs): + if mobject is None: + mobject = Line(3*LEFT, 3*RIGHT) + Animation.__init__(self, mobject, **kwargs) + + def wave_function(self, x, t): + return sum([ + reduce(op.mul, [ + self.amplitude/(k**2), #Amplitude + np.sin(2*np.pi*(k**1.5)*t/self.temporal_period), #Frequency + np.sin(2*np.pi*k*x/self.spatial_period) #Number of waves + ]) + for k in range(1, self.overtones+1) + ]) + + + def update_mobject(self, alpha): + time = alpha*self.run_time + families = map( + Mobject.submobject_family, + [self.mobject, self.starting_mobject] + ) + for mob, start in zip(*families): + mob.points = np.apply_along_axis( + lambda (x, y, z) : (x, y + self.wave_function(x, time), z), + 1, start.points + ) + +class TurnInsideOut(Transform): + CONFIG = { + "path_arc" : TAU/4, + } + def __init__(self, mobject, **kwargs): + mobject.sort_points(np.linalg.norm) + mob_copy = mobject.copy() + mob_copy.sort_points(lambda p : -np.linalg.norm(p)) + Transform.__init__(self, mobject, mob_copy, **kwargs) + + diff --git a/animation/movement.py b/animation/movement.py new file mode 100644 index 00000000..8842240f --- /dev/null +++ b/animation/movement.py @@ -0,0 +1,62 @@ +from __future__ import absolute_import + +from constants import * + +import warnings + +from animation.animation import Animation +from utils.config_ops import digest_config + +class Homotopy(Animation): + CONFIG = { + "run_time" : 3, + "apply_function_kwargs" : {}, + } + def __init__(self, homotopy, mobject, **kwargs): + """ + Homotopy a function from (x, y, z, t) to (x', y', z') + """ + def function_at_time_t(t): + return lambda p : homotopy(p[0], p[1], p[2], t) + self.function_at_time_t = function_at_time_t + digest_config(self, kwargs) + Animation.__init__(self, mobject, **kwargs) + + def update_submobject(self, submob, start, alpha): + submob.points = start.points + submob.apply_function( + self.function_at_time_t(alpha), + **self.apply_function_kwargs + ) + +class SmoothedVectorizedHomotopy(Homotopy): + def update_submobject(self, submob, start, alpha): + Homotopy.update_submobject(self, submob, start, alpha) + submob.make_smooth() + +class PhaseFlow(Animation): + CONFIG = { + "virtual_time" : 1, + "rate_func" : None, + } + def __init__(self, function, mobject, **kwargs): + digest_config(self, kwargs, locals()) + Animation.__init__(self, mobject, **kwargs) + + def update_mobject(self, alpha): + if hasattr(self, "last_alpha"): + dt = self.virtual_time*(alpha-self.last_alpha) + self.mobject.apply_function( + lambda p : p + dt*self.function(p) + ) + self.last_alpha = alpha + +class MoveAlongPath(Animation): + def __init__(self, mobject, path, **kwargs): + digest_config(self, kwargs, locals()) + Animation.__init__(self, mobject, **kwargs) + + def update_mobject(self, alpha): + point = self.path.point_from_proportion(alpha) + self.mobject.move_to(point) + diff --git a/animation/playground.py b/animation/playground.py deleted file mode 100644 index f876ba5d..00000000 --- a/animation/playground.py +++ /dev/null @@ -1,62 +0,0 @@ -import numpy as np -import operator as op - -from .animation import Animation -from transform import Transform -from mobject.mobject import Mobject -from mobject.point_cloud_mobject import Mobject1D -from topics.geometry import Line -from utils.paths import path_along_arc - -from constants import * - -class Vibrate(Animation): - CONFIG = { - "spatial_period" : 6, - "temporal_period" : 1, - "overtones" : 4, - "amplitude" : 0.5, - "radius" : FRAME_X_RADIUS/2, - "run_time" : 3.0, - "rate_func" : None - } - def __init__(self, mobject = None, **kwargs): - if mobject is None: - mobject = Line(3*LEFT, 3*RIGHT) - Animation.__init__(self, mobject, **kwargs) - - def wave_function(self, x, t): - return sum([ - reduce(op.mul, [ - self.amplitude/(k**2), #Amplitude - np.sin(2*np.pi*(k**1.5)*t/self.temporal_period), #Frequency - np.sin(2*np.pi*k*x/self.spatial_period) #Number of waves - ]) - for k in range(1, self.overtones+1) - ]) - - - def update_mobject(self, alpha): - time = alpha*self.run_time - families = map( - Mobject.submobject_family, - [self.mobject, self.starting_mobject] - ) - for mob, start in zip(*families): - mob.points = np.apply_along_axis( - lambda (x, y, z) : (x, y + self.wave_function(x, time), z), - 1, start.points - ) - - -class TurnInsideOut(Transform): - CONFIG = { - "path_func" : path_along_arc(np.pi/2) - } - def __init__(self, mobject, **kwargs): - mobject.sort_points(np.linalg.norm) - mob_copy = mobject.copy() - mob_copy.sort_points(lambda p : -np.linalg.norm(p)) - Transform.__init__(self, mobject, mob_copy, **kwargs) - - diff --git a/animation/rotation.py b/animation/rotation.py new file mode 100644 index 00000000..f2118741 --- /dev/null +++ b/animation/rotation.py @@ -0,0 +1,61 @@ +from __future__ import absolute_import + +import itertools as it +import numpy as np + +from constants import * + +import warnings + +from animation.animation import Animation +from animation.transform import Transform +from utils.config_ops import digest_config + +class Rotating(Animation): + CONFIG = { + "axis" : OUT, + "radians" : 2*np.pi, + "run_time" : 5, + "rate_func" : None, + "in_place" : True, + "about_point" : None, + "about_edge" : None, + } + def update_submobject(self, submobject, starting_submobject, alpha): + submobject.points = np.array(starting_submobject.points) + + def update_mobject(self, alpha): + Animation.update_mobject(self, alpha) + about_point = None + if self.about_point is not None: + about_point = self.about_point + elif self.in_place: #This is superseeded + self.about_point = self.mobject.get_center() + self.mobject.rotate( + alpha*self.radians, + axis = self.axis, + about_point = self.about_point, + about_edge = self.about_edge, + ) + +class Rotate(Transform): + CONFIG = { + "in_place" : False, + "about_point" : None, + } + def __init__(self, mobject, angle = np.pi, axis = OUT, **kwargs): + if "path_arc" not in kwargs: + kwargs["path_arc"] = angle + if "path_arc_axis" not in kwargs: + kwargs["path_arc_axis"] = axis + digest_config(self, kwargs, locals()) + target = mobject.copy() + if self.in_place: + self.about_point = mobject.get_center() + target.rotate( + angle, + axis = axis, + about_point = self.about_point, + ) + Transform.__init__(self, mobject, target, **kwargs) + diff --git a/animation/simple_animations.py b/animation/simple_animations.py deleted file mode 100644 index 61f9ad0c..00000000 --- a/animation/simple_animations.py +++ /dev/null @@ -1,336 +0,0 @@ -import numpy as np -import itertools as it - -from constants import * - -import warnings -from mobject.mobject import Mobject, Group -from mobject.vectorized_mobject import VMobject -from mobject.tex_mobject import TextMobject -from .animation import Animation -from transform import Transform -from utils.bezier import interpolate -from utils.config_ops import digest_config -from utils.rate_functions import smooth, double_smooth, there_and_back, wiggle - -class Rotating(Animation): - CONFIG = { - "axis" : OUT, - "radians" : 2*np.pi, - "run_time" : 5, - "rate_func" : None, - "in_place" : True, - "about_point" : None, - "about_edge" : None, - } - def update_submobject(self, submobject, starting_submobject, alpha): - submobject.points = np.array(starting_submobject.points) - - def update_mobject(self, alpha): - Animation.update_mobject(self, alpha) - about_point = None - if self.about_point is not None: - about_point = self.about_point - elif self.in_place: #This is superseeded - self.about_point = self.mobject.get_center() - self.mobject.rotate( - alpha*self.radians, - axis = self.axis, - about_point = self.about_point, - about_edge = self.about_edge, - ) - -class ShowPartial(Animation): - def update_submobject(self, submobject, starting_submobject, alpha): - submobject.pointwise_become_partial( - starting_submobject, *self.get_bounds(alpha) - ) - - def get_bounds(self, alpha): - raise Exception("Not Implemented") - -class ShowCreation(ShowPartial): - CONFIG = { - "submobject_mode" : "one_at_a_time", - } - def get_bounds(self, alpha): - return (0, alpha) - -class Uncreate(ShowCreation): - CONFIG = { - "rate_func" : lambda t : smooth(1-t), - "remover" : True - } - -class Write(ShowCreation): - CONFIG = { - "rate_func" : None, - "submobject_mode" : "lagged_start", - } - def __init__(self, mob_or_text, **kwargs): - digest_config(self, kwargs) - if isinstance(mob_or_text, str): - mobject = TextMobject(mob_or_text) - else: - mobject = mob_or_text - if "run_time" not in kwargs: - self.establish_run_time(mobject) - if "lag_factor" not in kwargs: - if len(mobject.family_members_with_points()) < 4: - min_lag_factor = 1 - else: - min_lag_factor = 2 - self.lag_factor = max(self.run_time - 1, min_lag_factor) - ShowCreation.__init__(self, mobject, **kwargs) - - def establish_run_time(self, mobject): - num_subs = len(mobject.family_members_with_points()) - if num_subs < 15: - self.run_time = 1 - else: - self.run_time = 2 - -class DrawBorderThenFill(Animation): - CONFIG = { - "run_time" : 2, - "stroke_width" : 2, - "stroke_color" : None, - "rate_func" : double_smooth, - } - def __init__(self, vmobject, **kwargs): - if not isinstance(vmobject, VMobject): - raise Exception("DrawBorderThenFill only works for VMobjects") - self.reached_halfway_point_before = False - Animation.__init__(self, vmobject, **kwargs) - - def update_submobject(self, submobject, starting_submobject, alpha): - submobject.pointwise_become_partial( - starting_submobject, 0, min(2*alpha, 1) - ) - if alpha < 0.5: - if self.stroke_color: - color = self.stroke_color - elif starting_submobject.stroke_width > 0: - color = starting_submobject.get_stroke_color() - else: - color = starting_submobject.get_color() - submobject.set_stroke(color, width = self.stroke_width) - submobject.set_fill(opacity = 0) - else: - if not self.reached_halfway_point_before: - self.reached_halfway_point_before = True - submobject.points = np.array(starting_submobject.points) - width, opacity = [ - interpolate(start, end, 2*alpha - 1) - for start, end in [ - (self.stroke_width, starting_submobject.get_stroke_width()), - (0, starting_submobject.get_fill_opacity()) - ] - ] - submobject.set_stroke(width = width) - submobject.set_fill(opacity = opacity) - -class ShowPassingFlash(ShowPartial): - CONFIG = { - "time_width" : 0.1, - "remover" : True, - } - def get_bounds(self, alpha): - alpha *= (1+self.time_width) - alpha -= self.time_width/2.0 - lower = max(0, alpha - self.time_width/2.0) - upper = min(1, alpha + self.time_width/2.0) - return (lower, upper) - - def clean_up(self, *args, **kwargs): - ShowPartial.clean_up(self, *args, **kwargs) - for submob, start_submob in self.get_all_families_zipped(): - submob.pointwise_become_partial(start_submob, 0, 1) - -class ShowCreationThenDestruction(ShowPassingFlash): - CONFIG = { - "time_width" : 2.0, - "run_time" : 1, - } - -class Homotopy(Animation): - CONFIG = { - "run_time" : 3, - "apply_function_kwargs" : {}, - } - def __init__(self, homotopy, mobject, **kwargs): - """ - Homotopy a function from (x, y, z, t) to (x', y', z') - """ - def function_at_time_t(t): - return lambda p : homotopy(p[0], p[1], p[2], t) - self.function_at_time_t = function_at_time_t - digest_config(self, kwargs) - Animation.__init__(self, mobject, **kwargs) - - def update_submobject(self, submob, start, alpha): - submob.points = start.points - submob.apply_function( - self.function_at_time_t(alpha), - **self.apply_function_kwargs - ) - -class SmoothedVectorizedHomotopy(Homotopy): - def update_submobject(self, submob, start, alpha): - Homotopy.update_submobject(self, submob, start, alpha) - submob.make_smooth() - -class ApplyWave(Homotopy): - CONFIG = { - "direction" : DOWN, - "amplitude" : 0.2, - "run_time" : 1, - "apply_function_kwargs" : { - "maintain_smoothness" : False, - }, - } - def __init__(self, mobject, **kwargs): - digest_config(self, kwargs, locals()) - left_x = mobject.get_left()[0] - right_x = mobject.get_right()[0] - vect = self.amplitude*self.direction - def homotopy(x, y, z, t): - start_point = np.array([x, y, z]) - alpha = (x-left_x)/(right_x-left_x) - power = np.exp(2*(alpha-0.5)) - nudge = there_and_back(t**power) - return np.array([x, y, z]) + nudge*vect - Homotopy.__init__(self, homotopy, mobject, **kwargs) - -class PhaseFlow(Animation): - CONFIG = { - "virtual_time" : 1, - "rate_func" : None, - } - def __init__(self, function, mobject, **kwargs): - digest_config(self, kwargs, locals()) - Animation.__init__(self, mobject, **kwargs) - - def update_mobject(self, alpha): - if hasattr(self, "last_alpha"): - dt = self.virtual_time*(alpha-self.last_alpha) - self.mobject.apply_function( - lambda p : p + dt*self.function(p) - ) - self.last_alpha = alpha - -class MoveAlongPath(Animation): - def __init__(self, mobject, path, **kwargs): - digest_config(self, kwargs, locals()) - Animation.__init__(self, mobject, **kwargs) - - def update_mobject(self, alpha): - point = self.path.point_from_proportion(alpha) - self.mobject.move_to(point) - -class UpdateFromFunc(Animation): - """ - update_function of the form func(mobject), presumably - to be used when the state of one mobject is dependent - on another simultaneously animated mobject - """ - def __init__(self, mobject, update_function, **kwargs): - digest_config(self, kwargs, locals()) - Animation.__init__(self, mobject, **kwargs) - - def update_mobject(self, alpha): - self.update_function(self.mobject) - -class UpdateFromAlphaFunc(UpdateFromFunc): - def update_mobject(self, alpha): - self.update_function(self.mobject, alpha) - -class MaintainPositionRelativeTo(Animation): - CONFIG = { - "tracked_critical_point" : ORIGIN - } - def __init__(self, mobject, tracked_mobject, **kwargs): - digest_config(self, kwargs, locals()) - tcp = self.tracked_critical_point - self.diff = mobject.get_critical_point(tcp) - \ - tracked_mobject.get_critical_point(tcp) - Animation.__init__(self, mobject, **kwargs) - - def update_mobject(self, alpha): - self.mobject.shift( - self.tracked_mobject.get_critical_point(self.tracked_critical_point) - \ - self.mobject.get_critical_point(self.tracked_critical_point) + \ - self.diff - ) - -class WiggleOutThenIn(Animation): - CONFIG = { - "scale_value" : 1.1, - "rotation_angle" : 0.01*TAU, - "n_wiggles" : 6, - "run_time" : 2, - "scale_about_point" : None, - "rotate_about_point" : None, - } - def __init__(self, mobject, **kwargs): - digest_config(self, kwargs) - if self.scale_about_point is None: - self.scale_about_point = mobject.get_center() - if self.rotate_about_point is None: - self.rotate_about_point = mobject.get_center() - Animation.__init__(self, mobject, **kwargs) - - def update_submobject(self, submobject, starting_sumobject, alpha): - submobject.points[:,:] = starting_sumobject.points - submobject.scale( - interpolate(1, self.scale_value, there_and_back(alpha)), - about_point = self.scale_about_point - ) - submobject.rotate( - wiggle(alpha, self.n_wiggles)*self.rotation_angle, - about_point = self.rotate_about_point - ) - -class ApplyToCenters(Animation): - def __init__(self, AnimationClass, mobjects, **kwargs): - full_kwargs = AnimationClass.CONFIG - full_kwargs.update(kwargs) - full_kwargs["mobject"] = Mobject(*[ - mob.get_point_mobject() - for mob in mobjects - ]) - self.centers_container = AnimationClass(**full_kwargs) - full_kwargs.pop("mobject") - Animation.__init__(self, Mobject(*mobjects), **full_kwargs) - self.name = str(self) + AnimationClass.__name__ - - def update_mobject(self, alpha): - self.centers_container.update_mobject(alpha) - center_mobs = self.centers_container.mobject.split() - mobjects = self.mobject.split() - for center_mob, mobject in zip(center_mobs, mobjects): - mobject.shift( - center_mob.get_center()-mobject.get_center() - ) - -class FadeInAndShiftFromDirection(Transform): - CONFIG = { - "direction" : DOWN, - } - def __init__(self, mobject, **kwargs): - digest_config(self, kwargs) - target = mobject.copy() - mobject.shift(self.direction) - mobject.fade(1) - Transform.__init__(self, mobject, target, **kwargs) - -# Essentially just a more convenient name for the above animation -class FadeInFromDown(FadeInAndShiftFromDirection): - CONFIG = { - "direction" : DOWN, - } - - - - - diff --git a/animation/transform.py b/animation/transform.py index 89d299c8..38778c28 100644 --- a/animation/transform.py +++ b/animation/transform.py @@ -1,19 +1,18 @@ -import numpy as np -import itertools as it +from __future__ import absolute_import + import inspect -import copy -import warnings +import numpy as np from constants import * -from .animation import Animation -from mobject.mobject import Mobject, Group -from mobject.vectorized_mobject import VMobject, VectorizedPoint -from topics.geometry import Dot, Circle +from animation.animation import Animation +from mobject.mobject import Group +from mobject.mobject import Mobject from utils.config_ops import digest_config from utils.iterables import adjacent_pairs -from utils.paths import straight_path, path_along_arc, counterclockwise_path -from utils.rate_functions import smooth, there_and_back +from utils.paths import path_along_arc +from utils.paths import straight_path +from utils.rate_functions import smooth from utils.rate_functions import squish_rate_func class Transform(Animation): @@ -74,7 +73,6 @@ class ReplacementTransform(Transform): "replace_mobject_with_target_in_scene" : True, } - class ClockwiseTransform(Transform): CONFIG = { "path_arc" : -np.pi @@ -91,54 +89,6 @@ class MoveToTarget(Transform): raise Exception("MoveToTarget called on mobject without attribute 'target' ") Transform.__init__(self, mobject, mobject.target, **kwargs) -class CyclicReplace(Transform): - CONFIG = { - "path_arc" : np.pi/2 - } - def __init__(self, *mobjects, **kwargs): - start = Group(*mobjects) - target = Group(*[ - m1.copy().move_to(m2) - for m1, m2 in adjacent_pairs(start) - ]) - Transform.__init__(self, start, target, **kwargs) - -class Swap(CyclicReplace): - pass #Renaming, more understandable for two entries - -class GrowFromPoint(Transform): - CONFIG = { - "point_color" : None, - } - def __init__(self, mobject, point, **kwargs): - digest_config(self, kwargs) - target = mobject.copy() - point_mob = VectorizedPoint(point) - if self.point_color: - point_mob.set_color(self.point_color) - mobject.replace(point_mob) - mobject.set_color(point_mob.get_color()) - Transform.__init__(self, mobject, target, **kwargs) - -class GrowFromCenter(GrowFromPoint): - def __init__(self, mobject, **kwargs): - GrowFromPoint.__init__(self, mobject, mobject.get_center(), **kwargs) - -class GrowArrow(GrowFromPoint): - def __init__(self, arrow, **kwargs): - GrowFromPoint.__init__(self, arrow, arrow.get_start(), **kwargs) - -class SpinInFromNothing(GrowFromCenter): - CONFIG = { - "path_func" : counterclockwise_path() - } - -class ShrinkToCenter(Transform): - def __init__(self, mobject, **kwargs): - Transform.__init__( - self, mobject, mobject.get_point_mobject(), **kwargs - ) - class ApplyMethod(Transform): CONFIG = { "submobject_mode" : "all_at_once" @@ -167,94 +117,6 @@ class ApplyMethod(Transform): method.im_func(target, *args, **method_kwargs) Transform.__init__(self, method.im_self, target, **kwargs) -class FadeOut(Transform): - CONFIG = { - "remover" : True, - } - def __init__(self, mobject, **kwargs): - target = mobject.copy() - target.fade(1) - Transform.__init__(self, mobject, target, **kwargs) - - def clean_up(self, surrounding_scene = None): - Transform.clean_up(self, surrounding_scene) - self.update(0) - -class FadeIn(Transform): - def __init__(self, mobject, **kwargs): - target = mobject.copy() - Transform.__init__(self, mobject, target, **kwargs) - self.starting_mobject.fade(1) - if isinstance(self.starting_mobject, VMobject): - self.starting_mobject.set_stroke(width = 0) - self.starting_mobject.set_fill(opacity = 0) - -class FocusOn(Transform): - CONFIG = { - "opacity" : 0.2, - "color" : GREY, - "run_time" : 2, - "remover" : True, - } - def __init__(self, mobject_or_point, **kwargs): - digest_config(self, kwargs) - big_dot = Dot( - radius = FRAME_X_RADIUS+FRAME_Y_RADIUS, - stroke_width = 0, - fill_color = self.color, - fill_opacity = 0, - ) - little_dot = Dot(radius = 0) - little_dot.set_fill(self.color, opacity = self.opacity) - little_dot.move_to(mobject_or_point) - - Transform.__init__(self, big_dot, little_dot, **kwargs) - -class Indicate(Transform): - CONFIG = { - "rate_func" : there_and_back, - "scale_factor" : 1.2, - "color" : YELLOW, - } - def __init__(self, mobject, **kwargs): - digest_config(self, kwargs) - target = mobject.copy() - target.scale_in_place(self.scale_factor) - target.set_color(self.color) - Transform.__init__(self, mobject, target, **kwargs) - -class CircleIndicate(Indicate): - CONFIG = { - "rate_func" : squish_rate_func(there_and_back, 0, 0.8), - "remover" : True - } - def __init__(self, mobject, **kwargs): - digest_config(self, kwargs) - circle = Circle(color = self.color, **kwargs) - circle.surround(mobject) - Indicate.__init__(self, circle, **kwargs) - -class Rotate(ApplyMethod): - CONFIG = { - "in_place" : False, - "about_point" : None, - } - def __init__(self, mobject, angle = np.pi, axis = OUT, **kwargs): - if "path_arc" not in kwargs: - kwargs["path_arc"] = angle - if "path_arc_axis" not in kwargs: - kwargs["path_arc_axis"] = axis - digest_config(self, kwargs, locals()) - target = mobject.copy() - if self.in_place: - self.about_point = mobject.get_center() - target.rotate( - angle, - axis = axis, - about_point = self.about_point, - ) - Transform.__init__(self, mobject, target, **kwargs) - class ApplyPointwiseFunction(ApplyMethod): CONFIG = { "run_time" : DEFAULT_POINTWISE_FUNCTION_RUN_TIME @@ -300,7 +162,22 @@ class ApplyMatrix(ApplyPointwiseFunction): return np.dot(p, transpose) ApplyPointwiseFunction.__init__(self, func, mobject, **kwargs) +class CyclicReplace(Transform): + CONFIG = { + "path_arc" : np.pi/2 + } + def __init__(self, *mobjects, **kwargs): + start = Group(*mobjects) + target = Group(*[ + m1.copy().move_to(m2) + for m1, m2 in adjacent_pairs(start) + ]) + Transform.__init__(self, start, target, **kwargs) +class Swap(CyclicReplace): + pass #Renaming, more understandable for two entries + +#TODO: Um...does this work class TransformAnimations(Transform): CONFIG = { "rate_func" : squish_rate_func(smooth) @@ -330,5 +207,3 @@ class TransformAnimations(Transform): self.end_anim.update(alpha) Transform.update(self, alpha) - - diff --git a/animation/update.py b/animation/update.py new file mode 100644 index 00000000..ff3ec543 --- /dev/null +++ b/animation/update.py @@ -0,0 +1,47 @@ +from __future__ import absolute_import + +from constants import * + +from animation.animation import Animation +from utils.config_ops import digest_config + + +class UpdateFromFunc(Animation): + """ + update_function of the form func(mobject), presumably + to be used when the state of one mobject is dependent + on another simultaneously animated mobject + """ + def __init__(self, mobject, update_function, **kwargs): + digest_config(self, kwargs, locals()) + Animation.__init__(self, mobject, **kwargs) + + def update_mobject(self, alpha): + self.update_function(self.mobject) + +class UpdateFromAlphaFunc(UpdateFromFunc): + def update_mobject(self, alpha): + self.update_function(self.mobject, alpha) + +class MaintainPositionRelativeTo(Animation): + CONFIG = { + "tracked_critical_point" : ORIGIN + } + def __init__(self, mobject, tracked_mobject, **kwargs): + digest_config(self, kwargs, locals()) + tcp = self.tracked_critical_point + self.diff = mobject.get_critical_point(tcp) - \ + tracked_mobject.get_critical_point(tcp) + Animation.__init__(self, mobject, **kwargs) + + def update_mobject(self, alpha): + self.mobject.shift( + self.tracked_mobject.get_critical_point(self.tracked_critical_point) - \ + self.mobject.get_critical_point(self.tracked_critical_point) + \ + self.diff + ) + + + + + diff --git a/big_ol_pile_of_manim_imports.py b/big_ol_pile_of_manim_imports.py index bd0a9c1b..36856deb 100644 --- a/big_ol_pile_of_manim_imports.py +++ b/big_ol_pile_of_manim_imports.py @@ -16,14 +16,18 @@ as a convenience for scripts createing scenes for videos from constants import * from animation.animation import * -from animation.compositions import * -from animation.continual_animation import * -from animation.playground import * -from animation.simple_animations import * +from animation.composition import * +from animation.creation import * +from animation.indication import * +from animation.movement import * +from animation.rotation import * from animation.transform import * +from animation.update import * from camera.camera import * +from continual_animation.continual_animation import * + from mobject.image_mobject import * from mobject.mobject import * from mobject.point_cloud_mobject import * @@ -35,7 +39,6 @@ from scene.moving_camera_scene import * from scene.reconfigurable_scene import * from scene.scene import * from scene.scene_from_video import * -from scene.tk_scene import * from scene.zoomed_scene import * from topics.arithmetic import * @@ -49,7 +52,6 @@ from topics.functions import * from topics.geometry import * from topics.graph_scene import * from topics.graph_theory import * -from topics.light import * from topics.matrix import * from topics.number_line import * from topics.numerals import * @@ -70,18 +72,17 @@ from utils.sounds import * from utils.space_ops import * from utils.strings import * -from special_animations import * - # Non manim libraries that are also nice to have without thinking -import numpy as np -import itertools as it -import operator as op -import random import inspect -import string -import re +import itertools as it +import numpy as np +import operator as op import os +import random +import re +import string + from PIL import Image from colour import Color diff --git a/camera/camera.py b/camera/camera.py index ea047fa5..d28449d4 100644 --- a/camera/camera.py +++ b/camera/camera.py @@ -1,23 +1,29 @@ -import numpy as np import itertools as it +import numpy as np import os -from PIL import Image -from colour import Color import aggdraw import copy import time +from PIL import Image +from colour import Color + from constants import * -from mobject.mobject import Mobject, Group +from mobject.image_mobject import ImageMobject +from mobject.mobject import Group +from mobject.mobject import Mobject from mobject.point_cloud_mobject import PMobject from mobject.vectorized_mobject import VMobject -from mobject.image_mobject import ImageMobject -from utils.color import rgb_to_hex, color_to_int_rgba -from utils.config_ops import digest_config, digest_locals, DictAsObject +from utils.color import color_to_int_rgba +from utils.color import rgb_to_hex +from utils.config_ops import DictAsObject +from utils.config_ops import digest_config +from utils.config_ops import digest_locals from utils.images import get_full_raster_image_path -from utils.iterables import remove_list_redundancies, list_difference_update from utils.iterables import batch_by_property +from utils.iterables import list_difference_update +from utils.iterables import remove_list_redundancies from utils.simple_functions import fdiv diff --git a/continual_animation/__init__.py b/continual_animation/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/animation/continual_animation.py b/continual_animation/continual_animation.py similarity index 98% rename from animation/continual_animation.py rename to continual_animation/continual_animation.py index fa3a21dc..6612297a 100644 --- a/animation/continual_animation.py +++ b/continual_animation/continual_animation.py @@ -1,6 +1,6 @@ from constants import * from mobject.mobject import Mobject, Group -from simple_animations import MaintainPositionRelativeTo +from animation.update import MaintainPositionRelativeTo import copy from utils.config_ops import instantiate from utils.config_ops import digest_config diff --git a/extract_scene.py b/extract_scene.py index 5f82c885..6e43b632 100644 --- a/extract_scene.py +++ b/extract_scene.py @@ -4,17 +4,18 @@ import sys # import getopt import argparse import imp -import itertools as it -import inspect -import traceback import imp +import inspect +import itertools as it import os import subprocess as sp +import traceback +from camera.camera import Camera from constants import * from scene.scene import Scene -from camera.camera import Camera -from utils.sounds import play_error_sound, play_finish_sound +from utils.sounds import play_error_sound +from utils.sounds import play_finish_sound HELP_MESSAGE = """ Usage: diff --git a/mobject/image_mobject.py b/mobject/image_mobject.py index 4c7daeb4..01a00c1d 100644 --- a/mobject/image_mobject.py +++ b/mobject/image_mobject.py @@ -1,12 +1,15 @@ -import numpy as np +from __future__ import absolute_import + import itertools as it +import numpy as np import os + from PIL import Image from random import random +from mobject.mobject import Mobject from constants import * -from .mobject import Mobject -from point_cloud_mobject import PMobject +from mobject.point_cloud_mobject import PMobject from utils.bezier import interpolate from utils.color import color_to_int_rgb from utils.color import interpolate_color diff --git a/mobject/mobject.py b/mobject/mobject.py index 16731950..1e67793b 100644 --- a/mobject/mobject.py +++ b/mobject/mobject.py @@ -1,20 +1,25 @@ +import copy +import itertools as it import numpy as np import operator as op -import itertools as it import os -import copy + from PIL import Image from colour import Color from constants import * from container.container import Container from utils.bezier import interpolate -from utils.color import color_to_rgb, color_gradient +from utils.color import color_gradient +from utils.color import color_to_rgb from utils.color import interpolate_color -from utils.iterables import remove_list_redundancies, list_update +from utils.iterables import list_update +from utils.iterables import remove_list_redundancies from utils.paths import straight_path -from utils.space_ops import rotation_matrix, angle_of_vector -from utils.space_ops import complex_to_R3, R3_to_complex +from utils.space_ops import R3_to_complex +from utils.space_ops import angle_of_vector +from utils.space_ops import complex_to_R3 +from utils.space_ops import rotation_matrix #TODO: Explain array_attrs diff --git a/mobject/point_cloud_mobject.py b/mobject/point_cloud_mobject.py index 5380b8f1..d1cb528e 100644 --- a/mobject/point_cloud_mobject.py +++ b/mobject/point_cloud_mobject.py @@ -1,9 +1,13 @@ +from __future__ import absolute_import + +from mobject.mobject import Mobject from constants import * -from .mobject import Mobject from utils.bezier import interpolate -from utils.color import color_to_rgb, color_to_rgba, rgba_to_color from utils.color import color_gradient +from utils.color import color_to_rgb +from utils.color import color_to_rgba from utils.color import interpolate_color +from utils.color import rgba_to_color from utils.config_ops import digest_config from utils.iterables import stretch_array_to_length diff --git a/mobject/svg_mobject.py b/mobject/svg_mobject.py index 3673d451..aacd74a8 100644 --- a/mobject/svg_mobject.py +++ b/mobject/svg_mobject.py @@ -1,14 +1,18 @@ -from xml.dom import minidom import itertools as it import re -import warnings import string +import warnings + +from xml.dom import minidom from constants import * -from vectorized_mobject import VMobject, VGroup -from topics.geometry import Rectangle, Circle +from topics.geometry import Circle +from topics.geometry import Rectangle from utils.bezier import is_closed -from utils.config_ops import digest_config, digest_locals +from utils.config_ops import digest_config +from utils.config_ops import digest_locals +from vectorized_mobject import VGroup +from vectorized_mobject import VMobject def string_to_numbers(num_string): num_string = num_string.replace("-",",-") diff --git a/mobject/tex_mobject.py b/mobject/tex_mobject.py index cf405697..b6c19f7c 100644 --- a/mobject/tex_mobject.py +++ b/mobject/tex_mobject.py @@ -1,13 +1,16 @@ from constants import * -from vectorized_mobject import VMobject, VGroup, VectorizedPoint -from svg_mobject import SVGMobject, VMobjectFromSVGPathstring +from svg_mobject import SVGMobject +from svg_mobject import VMobjectFromSVGPathstring from topics.geometry import BackgroundRectangle from utils.config_ops import digest_config +from vectorized_mobject import VGroup +from vectorized_mobject import VMobject +from vectorized_mobject import VectorizedPoint import collections -import sys import operator as op +import sys TEX_MOB_SCALE_FACTOR = 0.05 diff --git a/mobject/vectorized_mobject.py b/mobject/vectorized_mobject.py index 44d058f9..e4635f7a 100644 --- a/mobject/vectorized_mobject.py +++ b/mobject/vectorized_mobject.py @@ -1,10 +1,16 @@ +from __future__ import absolute_import + import re + from colour import Color +from mobject.mobject import Mobject from constants import * -from .mobject import Mobject -from utils.bezier import bezier, partial_bezier_points -from utils.bezier import interpolate, get_smooth_handle_points, is_closed +from utils.bezier import bezier +from utils.bezier import get_smooth_handle_points +from utils.bezier import interpolate +from utils.bezier import is_closed +from utils.bezier import partial_bezier_points from utils.color import color_to_rgb from utils.color import interpolate_color from utils.iterables import make_even diff --git a/old_projects/basel/basel.py b/old_projects/basel/basel.py index b45f18a7..2937ced5 100644 --- a/old_projects/basel/basel.py +++ b/old_projects/basel/basel.py @@ -1,31 +1,8 @@ #!/usr/bin/env python -from constants import * +from big_ol_pile_of_manim_imports import * -from mobject.tex_mobject import TexMobject -from mobject.mobject import Mobject -from mobject.image_mobject import ImageMobject -from mobject.vectorized_mobject import * - -from animation.animation import Animation -from animation.transform import * -from animation.simple_animations import * -from animation.compositions import * -from animation.continual_animation import * - -from animation.playground import * -from topics.geometry import * -from topics.characters import * -from topics.functions import * -from topics.number_line import * -from topics.numerals import * -from scene.scene import Scene -from camera.camera import Camera -from mobject.svg_mobject import * -from mobject.tex_mobject import * -from topics.three_dimensions import * - -from topics.light import * +from old_projects.basel.light import * import types import functools diff --git a/old_projects/basel/basel2.py b/old_projects/basel/basel2.py index c32044b5..b59dc470 100644 --- a/old_projects/basel/basel2.py +++ b/old_projects/basel/basel2.py @@ -2,6 +2,7 @@ # -*- coding: utf-8 -*- from big_ol_pile_of_manim_imports import * +from old_projects.basel.light import * import types import functools diff --git a/topics/light.py b/old_projects/basel/light.py similarity index 96% rename from topics/light.py rename to old_projects/basel/light.py index 67138c34..71b4895c 100644 --- a/topics/light.py +++ b/old_projects/basel/light.py @@ -1,31 +1,8 @@ -from constants import * - -from mobject.tex_mobject import TexMobject -from mobject.mobject import Mobject -from mobject.vectorized_mobject import * - -from animation.animation import Animation -from animation.transform import * -from animation.simple_animations import * -from animation.compositions import * -from animation.continual_animation import * - -from animation.playground import * -from topics.geometry import * -from topics.functions import * -from scene.scene import Scene -from camera.camera import Camera -from mobject.svg_mobject import * -from topics.three_dimensions import * +from big_ol_pile_of_manim_imports import * from scipy.spatial import ConvexHull from traceback import * -from utils.space_ops import rotation_matrix, z_to_vector -from utils.space_ops import rotate_vector, angle_between, angle_between_vectors -from utils.space_ops import project_along_vector - - LIGHT_COLOR = YELLOW SHADOW_COLOR = BLACK SWITCH_ON_RUN_TIME = 1.5 diff --git a/old_projects/nn/playground.py b/old_projects/nn/playground.py index 6537f294..478faeb4 100644 --- a/old_projects/nn/playground.py +++ b/old_projects/nn/playground.py @@ -15,7 +15,7 @@ from mobject.vectorized_mobject import * from animation.animation import Animation from animation.transform import * from animation.simple_animations import * -from animation.compositions import * +from animation.composition import * from animation.playground import * from animation.continual_animation import * from topics.geometry import * diff --git a/scene/__init__.py b/scene/__init__.py index f03541e4..8b137891 100644 --- a/scene/__init__.py +++ b/scene/__init__.py @@ -1,3 +1 @@ -__all__ = [ - "scene" -] + diff --git a/scene/moving_camera_scene.py b/scene/moving_camera_scene.py index d3e13776..27e3af0a 100644 --- a/scene/moving_camera_scene.py +++ b/scene/moving_camera_scene.py @@ -1,7 +1,9 @@ +from __future__ import absolute_import + from constants import * +from scene.scene import Scene from camera.camera import MovingCamera -from .scene import Scene from topics.geometry import ScreenRectangle class MovingCameraScene(Scene): diff --git a/scene/reconfigurable_scene.py b/scene/reconfigurable_scene.py index a747e5b8..555ad69d 100644 --- a/scene/reconfigurable_scene.py +++ b/scene/reconfigurable_scene.py @@ -1,6 +1,8 @@ +from __future__ import absolute_import + import numpy as np -from .scene import Scene +from scene.scene import Scene from animation.transform import Transform from mobject.mobject import Mobject diff --git a/scene/scene.py b/scene/scene.py index 601265eb..fb94161a 100644 --- a/scene/scene.py +++ b/scene/scene.py @@ -1,26 +1,26 @@ +import copy +import inspect +import itertools as it +import numpy as np +import os +import random +import shutil +import subprocess as sp +import time +import warnings + from PIL import Image from colour import Color -import numpy as np -import itertools as it -import warnings -import time -import os -import shutil -import copy from tqdm import tqdm as ProgressDisplay -import inspect -import subprocess as sp -import random from constants import * -from camera.camera import Camera -from tk_scene import TkSceneRoot -from mobject.mobject import Mobject -from mobject.vectorized_mobject import VMobject from animation.animation import Animation from animation.transform import MoveToTarget -from animation.continual_animation import ContinualAnimation +from camera.camera import Camera +from continual_animation.continual_animation import ContinualAnimation +from mobject.mobject import Mobject +from mobject.vectorized_mobject import VMobject from utils.iterables import list_update from container.container import Container @@ -538,9 +538,6 @@ class Scene(Container): self.update_frame(dont_update_when_skipping = False) self.get_image().show() - def preview(self): - TkSceneRoot(self) - def get_image_file_path(self, name = None, dont_update = False): folder = "images" if dont_update: diff --git a/scene/scene_from_video.py b/scene/scene_from_video.py index 9eff530a..aab35ade 100644 --- a/scene/scene_from_video.py +++ b/scene/scene_from_video.py @@ -1,9 +1,12 @@ -import numpy as np +from __future__ import absolute_import + import cv2 import itertools as it +import numpy as np + from tqdm import tqdm as show_progress -from .scene import Scene +from scene.scene import Scene class SceneFromVideo(Scene): diff --git a/scene/tk_scene.py b/scene/tk_scene.py deleted file mode 100644 index b79d4332..00000000 --- a/scene/tk_scene.py +++ /dev/null @@ -1,44 +0,0 @@ -import Tkinter -from PIL import ImageTk, Image -import itertools as it -import time - - -class TkSceneRoot(Tkinter.Tk): - def __init__(self, scene): - if scene.saved_frames == []: - raise Exception(str(scene) + " has no frames!") - Tkinter.Tk.__init__(self) - - kwargs = { - "height" : scene.camera.pixel_shape[0], - "width" : scene.camera.pixel_shape[1], - } - self.frame = Tkinter.Frame(self, **kwargs) - self.frame.pack() - self.canvas = Tkinter.Canvas(self.frame, **kwargs) - self.canvas.configure(background='black') - self.canvas.place(x=0, y=0) - - last_time = time.time() - for frame in it.cycle(scene.saved_frames): - # try: - # self.show_new_image(frame) - # except: - # break - self.show_new_image(frame) - sleep_time = scene.frame_duration - sleep_time -= time.time() - last_time - time.sleep(max(0, sleep_time)) - last_time = time.time() - self.mainloop() - - def show_new_image(self, frame): - image = Image.fromarray(frame.astype('uint8')).convert('RGB') - photo = ImageTk.PhotoImage(image) - self.canvas.delete(Tkinter.ALL) - self.canvas.create_image( - 0, 0, - image = photo, anchor = Tkinter.NW - ) - self.update() diff --git a/scene/zoomed_scene.py b/scene/zoomed_scene.py index 974e330f..7b1c9851 100644 --- a/scene/zoomed_scene.py +++ b/scene/zoomed_scene.py @@ -1,11 +1,13 @@ +from __future__ import absolute_import + import numpy as np -from .scene import Scene -from animation.transform import FadeIn -from mobject.mobject import Mobject -from topics.geometry import Rectangle +from scene.scene import Scene +from animation.creation import FadeIn from camera.camera import Camera from camera.camera import MovingCamera +from mobject.mobject import Mobject +from topics.geometry import Rectangle from constants import * diff --git a/stage_scenes.py b/stage_scenes.py index 7177d614..7e67220f 100644 --- a/stage_scenes.py +++ b/stage_scenes.py @@ -1,10 +1,13 @@ -import sys import inspect +import itertools as it import os import shutil -import itertools as it -from extract_scene import is_scene, get_module -from constants import ANIMATIONS_DIR, STAGED_SCENES_DIR +import sys + +from constants import ANIMATIONS_DIR +from constants import STAGED_SCENES_DIR +from extract_scene import get_module +from extract_scene import is_scene def get_sorted_scene_names(module_name): diff --git a/topics/__init__.py b/topics/__init__.py index 1ed0fc07..e69de29b 100644 --- a/topics/__init__.py +++ b/topics/__init__.py @@ -1,11 +0,0 @@ -__all__ = [ - "arithmetic", - "characters", - "combinatorics", - "complex_numbers", - "functions", - "geometry", - "graph_theory", - "number_line", - "three_dimensions", -] \ No newline at end of file diff --git a/topics/arithmetic.py b/topics/arithmetic.py index 6656621e..542e43c8 100644 --- a/topics/arithmetic.py +++ b/topics/arithmetic.py @@ -1,10 +1,10 @@ -import numpy as np import itertools as it +import numpy as np -from constants import * -from scene.scene import Scene from animation.animation import Animation +from constants import * from mobject.tex_mobject import TexMobject +from scene.scene import Scene class RearrangeEquation(Scene): def construct( diff --git a/topics/characters.py b/topics/characters.py index bca6b509..ffcbd668 100644 --- a/topics/characters.py +++ b/topics/characters.py @@ -1,25 +1,36 @@ -import random -import numpy as np import itertools as it +import numpy as np +import random from constants import * -from mobject.mobject import Mobject, Group +from mobject.mobject import Group +from mobject.mobject import Mobject from mobject.svg_mobject import SVGMobject -from mobject.vectorized_mobject import VMobject, VGroup -from mobject.tex_mobject import TextMobject, TexMobject +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.objects import Bubble, ThoughtBubble, SpeechBubble 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.transform import Transform, ApplyMethod, MoveToTarget -from animation.transform import ReplacementTransform, FadeOut, FadeIn -from animation.simple_animations import Write, ShowCreation -from animation.compositions import AnimationGroup +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 there_and_back, squish_rate_func +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") diff --git a/topics/combinatorics.py b/topics/combinatorics.py index 3986d52b..1ef0ceb1 100644 --- a/topics/combinatorics.py +++ b/topics/combinatorics.py @@ -1,7 +1,7 @@ from constants import * -from mobject.vectorized_mobject import VMobject from mobject.tex_mobject import TexMobject +from mobject.vectorized_mobject import VMobject from scene.scene import Scene from utils.simple_functions import choose diff --git a/topics/common_scenes.py b/topics/common_scenes.py index b72b75e9..34a8d89b 100644 --- a/topics/common_scenes.py +++ b/topics/common_scenes.py @@ -1,16 +1,24 @@ from constants import * -from scene.scene import Scene from animation.animation import Animation -from animation.simple_animations import Write, DrawBorderThenFill -from animation.compositions import LaggedStart -from animation.transform import FadeIn, FadeOut, ApplyMethod +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 from mobject.vectorized_mobject import VGroup -from mobject.tex_mobject import TexMobject, TextMobject -from topics.characters import Mortimer, Randolph, Blink +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 from topics.objects import PatreonLogo -from topics.geometry import Square, Rectangle, DashedLine class OpeningQuote(Scene): diff --git a/topics/complex_numbers.py b/topics/complex_numbers.py index 3f026703..437682d4 100644 --- a/topics/complex_numbers.py +++ b/topics/complex_numbers.py @@ -1,18 +1,22 @@ from constants import * -from mobject.vectorized_mobject import VGroup -from mobject.tex_mobject import TexMobject, TextMobject -from number_line import NumberPlane from animation.animation import Animation -from animation.transform import ApplyPointwiseFunction, MoveToTarget -from animation.simple_animations import Homotopy, ShowCreation, \ - SmoothedVectorizedHomotopy +from animation.movement import Homotopy +from animation.creation import ShowCreation +from animation.movement import SmoothedVectorizedHomotopy +from animation.transform import ApplyPointwiseFunction +from animation.transform import MoveToTarget +from mobject.tex_mobject import TexMobject +from mobject.tex_mobject import TextMobject +from mobject.vectorized_mobject import VGroup +from number_line import NumberPlane from scene.scene import Scene -from utils.config_ops import instantiate from utils.config_ops import digest_config +from utils.config_ops import instantiate from utils.paths import path_along_arc -from utils.space_ops import complex_to_R3, R3_to_complex +from utils.space_ops import R3_to_complex +from utils.space_ops import complex_to_R3 class ComplexTransformationScene(Scene): diff --git a/topics/counting.py b/topics/counting.py index 2da1911c..e8aa6dbb 100644 --- a/topics/counting.py +++ b/topics/counting.py @@ -1,12 +1,18 @@ from constants import * -from mobject.tex_mobject import TexMobject, TextMobject from mobject.mobject import Mobject -from mobject.vectorized_mobject import VMobject, VGroup +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 animation.transform import Transform, FadeIn, MoveToTarget -from animation.simple_animations import ShowCreation -from topics.geometry import Arrow, Circle, Dot +from animation.creation import ShowCreation +from animation.creation import FadeIn +from animation.transform import MoveToTarget +from animation.transform import Transform +from topics.geometry import Arrow +from topics.geometry import Circle +from topics.geometry import Dot # from topics.fractals import * from scene.scene import Scene diff --git a/topics/fractals.py b/topics/fractals.py index d8cc3909..1e405a67 100644 --- a/topics/fractals.py +++ b/topics/fractals.py @@ -1,14 +1,25 @@ # from mobject.mobject import Mobject, Point, Mobject1D -from mobject.vectorized_mobject import VMobject, VGroup, VectorizedPoint -from scene.scene import Scene +from animation.creation import ShowCreation from animation.transform import Transform -from animation.simple_animations import ShowCreation -from topics.geometry import Line, Polygon, RegularPolygon, Square, Circle -from characters import PiCreature, Randolph, get_all_pi_creature_modes +from characters import PiCreature +from characters import Randolph +from characters import get_all_pi_creature_modes +from mobject.vectorized_mobject import VGroup +from mobject.vectorized_mobject import VMobject +from mobject.vectorized_mobject import VectorizedPoint +from scene.scene import Scene +from topics.geometry import Circle +from topics.geometry import Line +from topics.geometry import Polygon +from topics.geometry import RegularPolygon +from topics.geometry import Square from utils.bezier import interpolate from utils.color import color_gradient from utils.config_ops import digest_config -from utils.space_ops import rotation_matrix, rotate_vector, compass_directions, center_of_mass +from utils.space_ops import center_of_mass +from utils.space_ops import compass_directions +from utils.space_ops import rotate_vector +from utils.space_ops import rotation_matrix from constants import * diff --git a/topics/geometry.py b/topics/geometry.py index fe98bb11..8be42047 100644 --- a/topics/geometry.py +++ b/topics/geometry.py @@ -4,11 +4,16 @@ import itertools as it import numpy as np from mobject.mobject import Mobject -from mobject.vectorized_mobject import VMobject, VGroup +from mobject.vectorized_mobject import VGroup +from mobject.vectorized_mobject import VMobject from utils.bezier import interpolate -from utils.config_ops import digest_config, digest_locals +from utils.config_ops import digest_config +from utils.config_ops import digest_locals from utils.paths import path_along_arc -from utils.space_ops import rotate_vector, angle_of_vector, compass_directions, center_of_mass +from utils.space_ops import angle_of_vector +from utils.space_ops import center_of_mass +from utils.space_ops import compass_directions +from utils.space_ops import rotate_vector class Arc(VMobject): CONFIG = { diff --git a/topics/graph_scene.py b/topics/graph_scene.py index ef3d92a7..84e2918f 100644 --- a/topics/graph_scene.py +++ b/topics/graph_scene.py @@ -2,16 +2,22 @@ from constants import * from scene.scene import Scene # from topics.geometry import -from mobject.tex_mobject import TexMobject, TextMobject -from mobject.vectorized_mobject import VGroup, VectorizedPoint -from animation.simple_animations import Write, ShowCreation, UpdateFromAlphaFunc +from animation.creation import ShowCreation +from animation.update import UpdateFromAlphaFunc +from animation.creation import Write from animation.transform import Transform -from topics.number_line import NumberLine +from mobject.tex_mobject import TexMobject +from mobject.tex_mobject import TextMobject +from mobject.vectorized_mobject import VGroup +from mobject.vectorized_mobject import VectorizedPoint from topics.functions import ParametricFunction -from topics.geometry import Rectangle, DashedLine, Line +from topics.geometry import DashedLine +from topics.geometry import Line +from topics.geometry import Rectangle +from topics.number_line import NumberLine from utils.bezier import interpolate -from utils.color import invert_color from utils.color import color_gradient +from utils.color import invert_color from utils.space_ops import angle_of_vector class GraphScene(Scene): diff --git a/topics/graph_theory.py b/topics/graph_theory.py index 19a4ba04..f3a7f6a1 100644 --- a/topics/graph_theory.py +++ b/topics/graph_theory.py @@ -1,6 +1,7 @@ import itertools as it import numpy as np import operator as op + from random import random from constants import * diff --git a/topics/matrix.py b/topics/matrix.py index e646fdba..936039f9 100644 --- a/topics/matrix.py +++ b/topics/matrix.py @@ -1,14 +1,26 @@ import numpy as np -from scene.scene import Scene +from animation.creation import ShowCreation +from animation.creation import Write +from animation.transform import ApplyFunction +from animation.transform import ApplyMethod +from animation.transform import ApplyPointwiseFunction +from animation.creation import FadeOut +from animation.transform import Transform from mobject.mobject import Mobject -from mobject.vectorized_mobject import VMobject, VGroup -from mobject.tex_mobject import TexMobject, TextMobject -from animation.transform import ApplyPointwiseFunction, Transform, \ - ApplyMethod, FadeOut, ApplyFunction -from animation.simple_animations import ShowCreation, Write -from topics.number_line import NumberPlane, Axes -from topics.geometry import Vector, Line, Circle, Arrow, Dot, BackgroundRectangle +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 scene.scene import Scene +from topics.geometry import Arrow +from topics.geometry import BackgroundRectangle +from topics.geometry import Circle +from topics.geometry import Dot +from topics.geometry import Line +from topics.geometry import Vector +from topics.number_line import Axes +from topics.number_line import NumberPlane from constants import * diff --git a/topics/number_line.py b/topics/number_line.py index 3a67ef85..7fcfefe4 100644 --- a/topics/number_line.py +++ b/topics/number_line.py @@ -1,10 +1,12 @@ from constants import * -from mobject.vectorized_mobject import VMobject, VGroup from mobject.tex_mobject import TexMobject -from topics.geometry import Line, Arrow -from topics.functions import ParametricFunction +from mobject.vectorized_mobject import VGroup +from mobject.vectorized_mobject import VMobject from scene.scene import Scene +from topics.functions import ParametricFunction +from topics.geometry import Arrow +from topics.geometry import Line from utils.bezier import interpolate from utils.config_ops import digest_config from utils.space_ops import angle_of_vector diff --git a/topics/numerals.py b/topics/numerals.py index f91cda5c..6ee454a7 100644 --- a/topics/numerals.py +++ b/topics/numerals.py @@ -1,11 +1,13 @@ -from mobject.vectorized_mobject import VMobject, VGroup, VectorizedPoint -from mobject.tex_mobject import TexMobject from animation.animation import Animation -from animation.continual_animation import ContinualAnimation -from topics.geometry import BackgroundRectangle -from scene.scene import Scene from constants import * +from continual_animation.continual_animation import ContinualAnimation +from mobject.tex_mobject import TexMobject +from mobject.vectorized_mobject import VGroup +from mobject.vectorized_mobject import VMobject +from mobject.vectorized_mobject import VectorizedPoint +from scene.scene import Scene +from topics.geometry import BackgroundRectangle from utils.bezier import interpolate from utils.config_ops import digest_config diff --git a/topics/objects.py b/topics/objects.py index f46f11e4..48a999e2 100644 --- a/topics/objects.py +++ b/topics/objects.py @@ -1,21 +1,36 @@ from constants import * from mobject.mobject import Mobject -from mobject.vectorized_mobject import VGroup, VMobject, VectorizedPoint from mobject.svg_mobject import SVGMobject -from mobject.tex_mobject import TextMobject, TexMobject, Brace +from mobject.tex_mobject import Brace +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 mobject.vectorized_mobject import VectorizedPoint from animation.animation import Animation -from animation.simple_animations import Rotating -from animation.compositions import LaggedStart, AnimationGroup -from animation.transform import ApplyMethod, FadeIn, GrowFromCenter +from animation.composition import AnimationGroup +from animation.composition import LaggedStart +from animation.rotation import Rotating +from animation.transform import ApplyMethod +from animation.creation import FadeIn +from animation.creation import GrowFromCenter -from topics.geometry import Circle, Line, Rectangle, Square, \ - Arc, Polygon, SurroundingRectangle +from topics.geometry import Arc +from topics.geometry import Circle +from topics.geometry import Line +from topics.geometry import Polygon +from topics.geometry import Rectangle +from topics.geometry import Square +from topics.geometry import SurroundingRectangle from topics.three_dimensions import Cube -from utils.config_ops import digest_config, digest_locals -from utils.space_ops import rotate_vector, angle_of_vector -from utils.space_ops import complex_to_R3, R3_to_complex +from utils.config_ops import digest_config +from utils.config_ops import digest_locals +from utils.space_ops import R3_to_complex +from utils.space_ops import angle_of_vector +from utils.space_ops import complex_to_R3 +from utils.space_ops import rotate_vector class Lightbulb(SVGMobject): CONFIG = { diff --git a/topics/probability.py b/topics/probability.py index 726c3050..8e086fb8 100644 --- a/topics/probability.py +++ b/topics/probability.py @@ -3,17 +3,28 @@ from constants import * from scene.scene import Scene from animation.animation import Animation -from animation.transform import Transform, MoveToTarget -from animation.simple_animations import UpdateFromFunc +from animation.transform import MoveToTarget +from animation.transform import Transform +from animation.update import UpdateFromFunc from mobject.mobject import Mobject -from mobject.vectorized_mobject import VGroup, VMobject, VectorizedPoint from mobject.svg_mobject import SVGMobject -from mobject.tex_mobject import TextMobject, TexMobject, Brace -from topics.geometry import Circle, Line, Rectangle, Square, Arc, Polygon +from mobject.tex_mobject import Brace +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 mobject.vectorized_mobject import VectorizedPoint +from topics.geometry import Arc +from topics.geometry import Circle +from topics.geometry import Line +from topics.geometry import Polygon +from topics.geometry import Rectangle +from topics.geometry import Square from utils.bezier import interpolate -from utils.color import color_gradient, average_color +from utils.color import average_color +from utils.color import color_gradient from utils.config_ops import digest_config from utils.iterables import tuplify from utils.space_ops import center_of_mass diff --git a/topics/three_dimensions.py b/topics/three_dimensions.py index ce02b651..37916087 100644 --- a/topics/three_dimensions.py +++ b/topics/three_dimensions.py @@ -1,16 +1,21 @@ from constants import * -from mobject.vectorized_mobject import VGroup, VMobject, VectorizedPoint -from topics.geometry import Square, Line -from scene.scene import Scene -from camera.camera import Camera -from animation.continual_animation import AmbientMovement +from continual_animation.continual_animation import AmbientMovement from animation.transform import ApplyMethod +from camera.camera import Camera +from mobject.vectorized_mobject import VGroup +from mobject.vectorized_mobject import VMobject +from mobject.vectorized_mobject import VectorizedPoint +from scene.scene import Scene +from topics.geometry import Line +from topics.geometry import Square from utils.bezier import interpolate from utils.iterables import list_update -from utils.space_ops import rotation_matrix, rotation_about_z, z_to_vector +from utils.space_ops import rotation_about_z +from utils.space_ops import rotation_matrix +from utils.space_ops import z_to_vector class CameraWithPerspective(Camera): diff --git a/topics/vector_space_scene.py b/topics/vector_space_scene.py index 081575e2..d7bdac44 100644 --- a/topics/vector_space_scene.py +++ b/topics/vector_space_scene.py @@ -1,20 +1,35 @@ import numpy as np -from scene.scene import Scene +from animation.animation import Animation +from animation.creation import ShowCreation +from animation.creation import Write +from animation.transform import ApplyFunction +from animation.transform import ApplyMethod +from animation.transform import ApplyPointwiseFunction +from animation.creation import FadeOut +from animation.transform import Transform from mobject.mobject import Mobject -from mobject.vectorized_mobject import VMobject, VGroup -from mobject.tex_mobject import TexMobject, TextMobject -from animation.animation import Animation -from animation.transform import ApplyPointwiseFunction, Transform, \ - ApplyMethod, FadeOut, ApplyFunction -from animation.simple_animations import ShowCreation, Write -from topics.number_line import NumberPlane, Axes -from topics.geometry import Vector, Line, Circle, Arrow, Dot, \ - BackgroundRectangle, Square +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 scene.scene import Scene +from topics.geometry import Arrow +from topics.geometry import BackgroundRectangle +from topics.geometry import Circle +from topics.geometry import Dot +from topics.geometry import Line +from topics.geometry import Square +from topics.geometry import Vector +from topics.number_line import Axes +from topics.number_line import NumberPlane from constants import * -from topics.matrix import Matrix, VECTOR_LABEL_SCALE_FACTOR, vector_coordinate_label -from utils.rate_functions import rush_into, rush_from +from topics.matrix import Matrix +from topics.matrix import VECTOR_LABEL_SCALE_FACTOR +from topics.matrix import vector_coordinate_label +from utils.rate_functions import rush_from +from utils.rate_functions import rush_into from utils.space_ops import angle_of_vector diff --git a/utils/bezier.py b/utils/bezier.py index 9bbecab8..cdce5198 100644 --- a/utils/bezier.py +++ b/utils/bezier.py @@ -1,4 +1,5 @@ import numpy as np + from scipy import linalg from utils.simple_functions import choose diff --git a/utils/color.py b/utils/color.py index 183c36f0..3933feac 100644 --- a/utils/color.py +++ b/utils/color.py @@ -1,7 +1,8 @@ -from colour import Color import numpy as np import random +from colour import Color + from utils.bezier import interpolate def color_to_rgb(color): diff --git a/utils/images.py b/utils/images.py index ea6ac556..9138166d 100644 --- a/utils/images.py +++ b/utils/images.py @@ -1,7 +1,8 @@ import numpy as np +import os + from PIL import Image from constants import RASTER_IMAGE_DIR -import os def get_full_raster_image_path(image_file_name): possible_paths = [ diff --git a/utils/iterables.py b/utils/iterables.py index f903ff42..f77aa45d 100644 --- a/utils/iterables.py +++ b/utils/iterables.py @@ -1,5 +1,5 @@ -import numpy as np import itertools as it +import numpy as np def remove_list_redundancies(l): """ diff --git a/utils/paths.py b/utils/paths.py index bf542c46..fd4e6694 100644 --- a/utils/paths.py +++ b/utils/paths.py @@ -1,7 +1,8 @@ import numpy as np + +from constants import OUT from utils.bezier import interpolate from utils.space_ops import rotation_matrix -from constants import OUT STRAIGHT_PATH_THRESHOLD = 0.01 diff --git a/utils/rate_functions.py b/utils/rate_functions.py index bce06815..566438ba 100644 --- a/utils/rate_functions.py +++ b/utils/rate_functions.py @@ -1,6 +1,7 @@ import numpy as np -from utils.simple_functions import sigmoid + from utils.bezier import bezier +from utils.simple_functions import sigmoid def smooth(t, inflection = 10.0): error = sigmoid(-inflection / 2) diff --git a/utils/simple_functions.py b/utils/simple_functions.py index ccdc0d50..52acc014 100644 --- a/utils/simple_functions.py +++ b/utils/simple_functions.py @@ -1,5 +1,5 @@ -import operator as op import numpy as np +import operator as op def sigmoid(x): return 1.0/(1 + np.exp(-x)) diff --git a/utils/space_ops.py b/utils/space_ops.py index 78d1c7a4..155273a6 100644 --- a/utils/space_ops.py +++ b/utils/space_ops.py @@ -1,5 +1,7 @@ import numpy as np -from constants import RIGHT, OUT + +from constants import OUT +from constants import RIGHT #Matrix operations From 087f66aca5999286823d4c215bfa2ed69b5f8efd Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Sat, 31 Mar 2018 15:20:30 -0700 Subject: [PATCH 02/17] Reorganized camera folder --- big_ol_pile_of_manim_imports.py | 2 + camera/camera.py | 129 -------------------------------- camera/mapping_camera.py | 104 +++++++++++++++++++++++++ camera/moving_camera.py | 32 ++++++++ scene/moving_camera_scene.py | 2 +- scene/zoomed_scene.py | 2 +- 6 files changed, 140 insertions(+), 131 deletions(-) create mode 100644 camera/mapping_camera.py create mode 100644 camera/moving_camera.py diff --git a/big_ol_pile_of_manim_imports.py b/big_ol_pile_of_manim_imports.py index 36856deb..33056dbc 100644 --- a/big_ol_pile_of_manim_imports.py +++ b/big_ol_pile_of_manim_imports.py @@ -25,6 +25,8 @@ from animation.transform import * from animation.update import * from camera.camera import * +from camera.moving_camera import * +from camera.mapping_camera import * from continual_animation.continual_animation import * diff --git a/camera/camera.py b/camera/camera.py index d28449d4..aba89340 100644 --- a/camera/camera.py +++ b/camera/camera.py @@ -11,22 +11,18 @@ from colour import Color from constants import * from mobject.image_mobject import ImageMobject -from mobject.mobject import Group from mobject.mobject import Mobject from mobject.point_cloud_mobject import PMobject from mobject.vectorized_mobject import VMobject from utils.color import color_to_int_rgba from utils.color import rgb_to_hex -from utils.config_ops import DictAsObject from utils.config_ops import digest_config -from utils.config_ops import digest_locals from utils.images import get_full_raster_image_path from utils.iterables import batch_by_property from utils.iterables import list_difference_update from utils.iterables import remove_list_redundancies from utils.simple_functions import fdiv - class Camera(object): CONFIG = { "background_image" : None, @@ -613,128 +609,3 @@ class BackgroundColoredVMobjectDisplayer(object): self.reset_canvas() return curr_array - -class MovingCamera(Camera): - """ - Stays in line with the height, width and position - of a given mobject - """ - CONFIG = { - "aligned_dimension" : "width" #or height - } - def __init__(self, mobject, **kwargs): - digest_locals(self) - Camera.__init__(self, **kwargs) - - def capture_mobjects(self, *args, **kwargs): - self.space_center = self.mobject.get_center() - self.realign_frame_shape() - Camera.capture_mobjects(self, *args, **kwargs) - - def realign_frame_shape(self): - height, width = self.frame_shape - if self.aligned_dimension == "height": - self.frame_shape = (self.mobject.get_height(), width) - else: - self.frame_shape = (height, self.mobject.get_width()) - self.resize_frame_shape(0 if self.aligned_dimension == "height" else 1) - -# TODO: Add an attribute to mobjects under which they can specify that they should just -# map their centers but remain otherwise undistorted (useful for labels, etc.) -class MappingCamera(Camera): - CONFIG = { - "mapping_func" : lambda p : p, - "min_anchor_points" : 50, - "allow_object_intrusion" : False - } - - def points_to_pixel_coords(self, points): - return Camera.points_to_pixel_coords(self, np.apply_along_axis(self.mapping_func, 1, points)) - - def capture_mobjects(self, mobjects, **kwargs): - mobjects = self.get_mobjects_to_display(mobjects, **kwargs) - if self.allow_object_intrusion: - mobject_copies = mobjects - else: - mobject_copies = [mobject.copy() for mobject in mobjects] - for mobject in mobject_copies: - if isinstance(mobject, VMobject) and \ - 0 < mobject.get_num_anchor_points() < self.min_anchor_points: - mobject.insert_n_anchor_points(self.min_anchor_points) - Camera.capture_mobjects( - self, mobject_copies, - include_submobjects = False, - excluded_mobjects = None, - ) - -# Note: This allows layering of multiple cameras onto the same portion of the pixel array, -# the later cameras overwriting the former -# -# TODO: Add optional separator borders between cameras (or perhaps peel this off into a -# CameraPlusOverlay class) -class MultiCamera(Camera): - def __init__(self, *cameras_with_start_positions, **kwargs): - self.shifted_cameras = [ - DictAsObject( - { - "camera" : camera_with_start_positions[0], - "start_x" : camera_with_start_positions[1][1], - "start_y" : camera_with_start_positions[1][0], - "end_x" : camera_with_start_positions[1][1] + camera_with_start_positions[0].pixel_shape[1], - "end_y" : camera_with_start_positions[1][0] + camera_with_start_positions[0].pixel_shape[0], - }) - for camera_with_start_positions in cameras_with_start_positions - ] - Camera.__init__(self, **kwargs) - - def capture_mobjects(self, mobjects, **kwargs): - for shifted_camera in self.shifted_cameras: - shifted_camera.camera.capture_mobjects(mobjects, **kwargs) - - self.pixel_array[ - shifted_camera.start_y:shifted_camera.end_y, - shifted_camera.start_x:shifted_camera.end_x] \ - = shifted_camera.camera.pixel_array - - def set_background(self, pixel_array, **kwargs): - for shifted_camera in self.shifted_cameras: - shifted_camera.camera.set_background( - pixel_array[ - shifted_camera.start_y:shifted_camera.end_y, - shifted_camera.start_x:shifted_camera.end_x], - **kwargs - ) - - def set_pixel_array(self, pixel_array, **kwargs): - Camera.set_pixel_array(self, pixel_array, **kwargs) - for shifted_camera in self.shifted_cameras: - shifted_camera.camera.set_pixel_array( - pixel_array[ - shifted_camera.start_y:shifted_camera.end_y, - shifted_camera.start_x:shifted_camera.end_x], - **kwargs - ) - - def init_background(self): - Camera.init_background(self) - for shifted_camera in self.shifted_cameras: - shifted_camera.camera.init_background() - -# A MultiCamera which, when called with two full-size cameras, initializes itself -# as a splitscreen, also taking care to resize each individual camera within it -class SplitScreenCamera(MultiCamera): - def __init__(self, left_camera, right_camera, **kwargs): - digest_config(self, kwargs) - self.left_camera = left_camera - self.right_camera = right_camera - - half_width = self.pixel_shape[1] / 2 - for camera in [self.left_camera, self.right_camera]: - camera.pixel_shape = (self.pixel_shape[0], half_width) # TODO: Round up on one if width is odd - camera.init_background() - camera.resize_frame_shape() - camera.reset() - - MultiCamera.__init__(self, (left_camera, (0, 0)), (right_camera, (0, half_width))) - - diff --git a/camera/mapping_camera.py b/camera/mapping_camera.py new file mode 100644 index 00000000..66e38adb --- /dev/null +++ b/camera/mapping_camera.py @@ -0,0 +1,104 @@ +from __future__ import absolute_import + +from camera.camera import Camera +from utils.config_ops import DictAsObject +from utils.config_ops import digest_config + +# TODO: Add an attribute to mobjects under which they can specify that they should just +# map their centers but remain otherwise undistorted (useful for labels, etc.) +class MappingCamera(Camera): + CONFIG = { + "mapping_func" : lambda p : p, + "min_anchor_points" : 50, + "allow_object_intrusion" : False + } + + def points_to_pixel_coords(self, points): + return Camera.points_to_pixel_coords(self, np.apply_along_axis(self.mapping_func, 1, points)) + + def capture_mobjects(self, mobjects, **kwargs): + mobjects = self.get_mobjects_to_display(mobjects, **kwargs) + if self.allow_object_intrusion: + mobject_copies = mobjects + else: + mobject_copies = [mobject.copy() for mobject in mobjects] + for mobject in mobject_copies: + if isinstance(mobject, VMobject) and \ + 0 < mobject.get_num_anchor_points() < self.min_anchor_points: + mobject.insert_n_anchor_points(self.min_anchor_points) + Camera.capture_mobjects( + self, mobject_copies, + include_submobjects = False, + excluded_mobjects = None, + ) + +# Note: This allows layering of multiple cameras onto the same portion of the pixel array, +# the later cameras overwriting the former +# +# TODO: Add optional separator borders between cameras (or perhaps peel this off into a +# CameraPlusOverlay class) +class MultiCamera(Camera): + def __init__(self, *cameras_with_start_positions, **kwargs): + self.shifted_cameras = [ + DictAsObject( + { + "camera" : camera_with_start_positions[0], + "start_x" : camera_with_start_positions[1][1], + "start_y" : camera_with_start_positions[1][0], + "end_x" : camera_with_start_positions[1][1] + camera_with_start_positions[0].pixel_shape[1], + "end_y" : camera_with_start_positions[1][0] + camera_with_start_positions[0].pixel_shape[0], + }) + for camera_with_start_positions in cameras_with_start_positions + ] + Camera.__init__(self, **kwargs) + + def capture_mobjects(self, mobjects, **kwargs): + for shifted_camera in self.shifted_cameras: + shifted_camera.camera.capture_mobjects(mobjects, **kwargs) + + self.pixel_array[ + shifted_camera.start_y:shifted_camera.end_y, + shifted_camera.start_x:shifted_camera.end_x] \ + = shifted_camera.camera.pixel_array + + def set_background(self, pixel_array, **kwargs): + for shifted_camera in self.shifted_cameras: + shifted_camera.camera.set_background( + pixel_array[ + shifted_camera.start_y:shifted_camera.end_y, + shifted_camera.start_x:shifted_camera.end_x], + **kwargs + ) + + def set_pixel_array(self, pixel_array, **kwargs): + Camera.set_pixel_array(self, pixel_array, **kwargs) + for shifted_camera in self.shifted_cameras: + shifted_camera.camera.set_pixel_array( + pixel_array[ + shifted_camera.start_y:shifted_camera.end_y, + shifted_camera.start_x:shifted_camera.end_x], + **kwargs + ) + + def init_background(self): + Camera.init_background(self) + for shifted_camera in self.shifted_cameras: + shifted_camera.camera.init_background() + +# A MultiCamera which, when called with two full-size cameras, initializes itself +# as a splitscreen, also taking care to resize each individual camera within it +class SplitScreenCamera(MultiCamera): + def __init__(self, left_camera, right_camera, **kwargs): + digest_config(self, kwargs) + self.left_camera = left_camera + self.right_camera = right_camera + + half_width = self.pixel_shape[1] / 2 + for camera in [self.left_camera, self.right_camera]: + camera.pixel_shape = (self.pixel_shape[0], half_width) # TODO: Round up on one if width is odd + camera.init_background() + camera.resize_frame_shape() + camera.reset() + + MultiCamera.__init__(self, (left_camera, (0, 0)), (right_camera, (0, half_width))) + diff --git a/camera/moving_camera.py b/camera/moving_camera.py new file mode 100644 index 00000000..f02c4012 --- /dev/null +++ b/camera/moving_camera.py @@ -0,0 +1,32 @@ +from __future__ import absolute_import + +from camera.camera import Camera + +class MovingCamera(Camera): + """ + Stays in line with the height, width and position + of a given mobject + """ + CONFIG = { + "aligned_dimension" : "width" #or height + } + def __init__(self, frame, **kwargs): + """ + frame is a Mobject, (should be a rectangle) determining + which region of space the camera displys + """ + self.frame = frame + Camera.__init__(self, **kwargs) + + def capture_mobjects(self, *args, **kwargs): + self.space_center = self.frame.get_center() + self.realign_frame_shape() + Camera.capture_mobjects(self, *args, **kwargs) + + def realign_frame_shape(self): + height, width = self.frame_shape + if self.aligned_dimension == "height": + self.frame_shape = (self.frame.get_height(), width) + else: + self.frame_shape = (height, self.frame.get_width()) + self.resize_frame_shape(0 if self.aligned_dimension == "height" else 1) diff --git a/scene/moving_camera_scene.py b/scene/moving_camera_scene.py index 27e3af0a..61615984 100644 --- a/scene/moving_camera_scene.py +++ b/scene/moving_camera_scene.py @@ -3,7 +3,7 @@ from __future__ import absolute_import from constants import * from scene.scene import Scene -from camera.camera import MovingCamera +from camera.moving_camera import MovingCamera from topics.geometry import ScreenRectangle class MovingCameraScene(Scene): diff --git a/scene/zoomed_scene.py b/scene/zoomed_scene.py index 7b1c9851..e85e27c4 100644 --- a/scene/zoomed_scene.py +++ b/scene/zoomed_scene.py @@ -5,7 +5,7 @@ import numpy as np from scene.scene import Scene from animation.creation import FadeIn from camera.camera import Camera -from camera.camera import MovingCamera +from camera.moving_camera import MovingCamera from mobject.mobject import Mobject from topics.geometry import Rectangle From 846bd1065c46cde7713b6a1b84b414f2d6ba6dff Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Sat, 31 Mar 2018 15:37:23 -0700 Subject: [PATCH 03/17] Reorganized continual_animation folder --- big_ol_pile_of_manim_imports.py | 2 + continual_animation/continual_animation.py | 66 +++------------------- continual_animation/from_animation.py | 28 +++++++++ continual_animation/update.py | 33 +++++++++++ mobject/image_mobject.py | 3 +- mobject/region.py | 3 +- mobject/svg_mobject.py | 2 - old_projects/WindingNumber_G.py | 4 +- old_projects/basel/basel2.py | 2 +- old_projects/fourier.py | 4 +- old_projects/mug.py | 4 +- old_projects/uncertainty.py | 20 +++---- topics/three_dimensions.py | 4 +- 13 files changed, 93 insertions(+), 82 deletions(-) create mode 100644 continual_animation/from_animation.py create mode 100644 continual_animation/update.py diff --git a/big_ol_pile_of_manim_imports.py b/big_ol_pile_of_manim_imports.py index 33056dbc..0977cc5d 100644 --- a/big_ol_pile_of_manim_imports.py +++ b/big_ol_pile_of_manim_imports.py @@ -29,6 +29,8 @@ from camera.moving_camera import * from camera.mapping_camera import * from continual_animation.continual_animation import * +from continual_animation.from_animation import * +from continual_animation.update import * from mobject.image_mobject import * from mobject.mobject import * diff --git a/continual_animation/continual_animation.py b/continual_animation/continual_animation.py index 6612297a..814b00c1 100644 --- a/continual_animation/continual_animation.py +++ b/continual_animation/continual_animation.py @@ -1,9 +1,10 @@ -from constants import * -from mobject.mobject import Mobject, Group -from animation.update import MaintainPositionRelativeTo import copy -from utils.config_ops import instantiate + +from constants import * +from mobject.mobject import Group +from mobject.mobject import Mobject from utils.config_ops import digest_config +from utils.config_ops import instantiate class ContinualAnimation(object): CONFIG = { @@ -66,7 +67,7 @@ class ContinualAnimationGroup(ContinualAnimation): for continual_animation in self.continual_animations: continual_animation.update(dt) -class AmbientRotation(ContinualAnimation): +class ContinualRotation(ContinualAnimation): CONFIG = { "axis" : OUT, "rate" : np.pi/12, #Radians per second @@ -86,7 +87,7 @@ class AmbientRotation(ContinualAnimation): about_point = about_point ) -class AmbientMovement(ContinualAnimation): +class ContinualMovement(ContinualAnimation): CONFIG = { "direction" : RIGHT, "rate" : 0.05, #Units per second @@ -95,59 +96,6 @@ class AmbientMovement(ContinualAnimation): def update_mobject(self, dt): self.mobject.shift(dt*self.rate*self.direction) -class ContinualUpdateFromFunc(ContinualAnimation): - CONFIG = { - "function_depends_on_dt" : False - } - def __init__(self, mobject, func, **kwargs): - self.func = func - ContinualAnimation.__init__(self, mobject, **kwargs) - - def update_mobject(self, dt): - if self.function_depends_on_dt: - self.func(self.mobject, dt) - else: - self.func(self.mobject) - -class ContinualUpdateFromTimeFunc(ContinualUpdateFromFunc): - CONFIG = { - "function_depends_on_dt" : True - } - -class ContinualMaintainPositionRelativeTo(ContinualAnimation): - # TODO: Possibly reimplement using CycleAnimation? - def __init__(self, mobject, tracked_mobject, **kwargs): - self.anim = MaintainPositionRelativeTo(mobject, tracked_mobject, **kwargs) - ContinualAnimation.__init__(self, mobject, **kwargs) - - def update_mobject(self, dt): - self.anim.update(0) # 0 is arbitrary - -class NormalAnimationAsContinualAnimation(ContinualAnimation): - CONFIG = { - "start_up_time" : 0, - "wind_down_time" : 0, - } - def __init__(self, animation, **kwargs): - self.animation = animation - ContinualAnimation.__init__(self, animation.mobject, **kwargs) - - def update_mobject(self, dt): - self.animation.update( - min(float(self.internal_time)/self.animation.run_time, 1) - ) - -class CycleAnimation(ContinualAnimation): - def __init__(self, animation, **kwargs): - self.animation = animation - ContinualAnimation.__init__(self, animation.mobject, **kwargs) - - def update_mobject(self, dt): - mod_value = self.internal_time % self.animation.run_time - alpha = mod_value/float(self.animation.run_time) - self.animation.update(alpha) - - diff --git a/continual_animation/from_animation.py b/continual_animation/from_animation.py new file mode 100644 index 00000000..bcbdc0a2 --- /dev/null +++ b/continual_animation/from_animation.py @@ -0,0 +1,28 @@ +from __future__ import absolute_import + +from continual_animation.continual_animation import ContinualAnimation + +class NormalAnimationAsContinualAnimation(ContinualAnimation): + CONFIG = { + "start_up_time" : 0, + "wind_down_time" : 0, + } + def __init__(self, animation, **kwargs): + self.animation = animation + ContinualAnimation.__init__(self, animation.mobject, **kwargs) + + def update_mobject(self, dt): + self.animation.update( + min(float(self.internal_time)/self.animation.run_time, 1) + ) + +class CycleAnimation(ContinualAnimation): + def __init__(self, animation, **kwargs): + self.animation = animation + ContinualAnimation.__init__(self, animation.mobject, **kwargs) + + def update_mobject(self, dt): + mod_value = self.internal_time % self.animation.run_time + alpha = mod_value/float(self.animation.run_time) + self.animation.update(alpha) + diff --git a/continual_animation/update.py b/continual_animation/update.py new file mode 100644 index 00000000..d891c11c --- /dev/null +++ b/continual_animation/update.py @@ -0,0 +1,33 @@ +from __future__ import absolute_import + +from continual_animation.continual_animation import ContinualAnimation +from animation.update import MaintainPositionRelativeTo + + +class ContinualUpdateFromFunc(ContinualAnimation): + CONFIG = { + "function_depends_on_dt" : False + } + def __init__(self, mobject, func, **kwargs): + self.func = func + ContinualAnimation.__init__(self, mobject, **kwargs) + + def update_mobject(self, dt): + if self.function_depends_on_dt: + self.func(self.mobject, dt) + else: + self.func(self.mobject) + +class ContinualUpdateFromTimeFunc(ContinualUpdateFromFunc): + CONFIG = { + "function_depends_on_dt" : True + } + +class ContinualMaintainPositionRelativeTo(ContinualAnimation): + # TODO: Possibly reimplement using CycleAnimation? + def __init__(self, mobject, tracked_mobject, **kwargs): + self.anim = MaintainPositionRelativeTo(mobject, tracked_mobject, **kwargs) + ContinualAnimation.__init__(self, mobject, **kwargs) + + def update_mobject(self, dt): + self.anim.update(0) # 0 is arbitrary diff --git a/mobject/image_mobject.py b/mobject/image_mobject.py index 01a00c1d..9dac5227 100644 --- a/mobject/image_mobject.py +++ b/mobject/image_mobject.py @@ -7,8 +7,9 @@ import os from PIL import Image from random import random -from mobject.mobject import Mobject from constants import * + +from mobject.mobject import Mobject from mobject.point_cloud_mobject import PMobject from utils.bezier import interpolate from utils.color import color_to_int_rgb diff --git a/mobject/region.py b/mobject/region.py index a23a02e0..13fac5be 100644 --- a/mobject/region.py +++ b/mobject/region.py @@ -8,7 +8,8 @@ from utils.iterables import adjacent_pairs from constants import * -#TODO, this whole class should be something vectorized. +# Warning: This is all now pretty depricated, and should not be expected to work + class Region(Mobject): CONFIG = { "display_mode" : "region" diff --git a/mobject/svg_mobject.py b/mobject/svg_mobject.py index aacd74a8..3644bd07 100644 --- a/mobject/svg_mobject.py +++ b/mobject/svg_mobject.py @@ -231,8 +231,6 @@ class SVGMobject(VMobject): if self.width is not None: self.scale_to_fit_width(self.width) - - class VMobjectFromSVGPathstring(VMobject): def __init__(self, path_string, **kwargs): digest_locals(self) diff --git a/old_projects/WindingNumber_G.py b/old_projects/WindingNumber_G.py index 377e5ac3..501fc289 100644 --- a/old_projects/WindingNumber_G.py +++ b/old_projects/WindingNumber_G.py @@ -1146,7 +1146,7 @@ class TwoDScreenInOurThreeDWorld(AltTeacherStudentsScene, ThreeDScene): run_time = 4, added_anims = [MoveToTarget(everything, run_time = 4)], ) - self.add(AmbientRotation(everything, axis = UP, rate = 3*DEGREES)) + self.add(ContinualRotation(everything, axis = UP, rate = 3*DEGREES)) self.wait(10) class EveryOutputPointHasAColor(ColorMappedObjectsScene): @@ -3188,7 +3188,7 @@ class PatreonScroll(Scene): # patorons = patrons[:10] ##TO remove - scroll = AmbientMovement(patrons, direction = UP, rate = 1) + scroll = ContinualMovement(patrons, direction = UP, rate = 1) def patrons_opacity_update(patrons): for patron in patrons: y = patron.get_center()[1] diff --git a/old_projects/basel/basel2.py b/old_projects/basel/basel2.py index b59dc470..55e8cd72 100644 --- a/old_projects/basel/basel2.py +++ b/old_projects/basel/basel2.py @@ -3848,7 +3848,7 @@ class ThinkBackToHowAmazingThisIs(ThreeDScene): for n in range(0, self.max_shown_n, 2) ]) - zoom_out = AmbientMovement( + zoom_out = ContinualMovement( self.camera.rotation_mobject, direction = OUT, rate = 0.4 ) diff --git a/old_projects/fourier.py b/old_projects/fourier.py index ea7619bb..571bd341 100644 --- a/old_projects/fourier.py +++ b/old_projects/fourier.py @@ -2874,7 +2874,7 @@ class WriteComplexExponentialExpression(DrawFrequencyPlot): self.wait() ghost_dot.move_to(ORIGIN) - ambient_ghost_dot_movement = AmbientMovement( + ambient_ghost_dot_movement = ContinualMovement( ghost_dot, rate = TAU ) self.add(ambient_ghost_dot_movement) @@ -2896,7 +2896,7 @@ class WriteComplexExponentialExpression(DrawFrequencyPlot): ) ) ghost_dot.move_to(ORIGIN) - ambient_ghost_dot_movement = AmbientMovement( + ambient_ghost_dot_movement = ContinualMovement( ghost_dot, rate = 0.1*TAU ) self.add(ambient_ghost_dot_movement) diff --git a/old_projects/mug.py b/old_projects/mug.py index 9bc48baa..fb8f6227 100644 --- a/old_projects/mug.py +++ b/old_projects/mug.py @@ -288,7 +288,7 @@ class AboutToyPuzzles(UtilitiesPuzzleScene, TeacherStudentsScene, ThreeDScene): eulers.get_bottom(), color = WHITE ) - self.add(AmbientRotation(cube, axis = UP)) + self.add(ContinualRotation(cube, axis = UP)) self.play( GrowArrow(arrow_to_eulers), Write(eulers), @@ -1884,7 +1884,7 @@ class EulersFormulaForGeneralPlanarGraph(LightUpNodes, ThreeDScene): self.play(FadeOut(self.vertices)) self.play(ReplacementTransform(regions, cube, run_time = 2)) cube.sort_submobjects(lambda p : -p[2]) - self.add(AmbientRotation(cube, axis = UP, in_place = False)) + self.add(ContinualRotation(cube, axis = UP, in_place = False)) self.wait(3) self.play( FadeOut(self.top_formula), diff --git a/old_projects/uncertainty.py b/old_projects/uncertainty.py index 2c69cd3f..cf353c32 100644 --- a/old_projects/uncertainty.py +++ b/old_projects/uncertainty.py @@ -608,7 +608,7 @@ class ShowPlan(PiCreatureScene): rect = BackgroundRectangle(wave, fill_opacity = 1) rect.stretch(2, 1) rect.next_to(wave, LEFT, buff = 0) - wave_shift = AmbientMovement( + wave_shift = ContinualMovement( wave, direction = LEFT, rate = 5 ) wave_fader = UpdateFromAlphaFunc( @@ -644,7 +644,7 @@ class ShowPlan(PiCreatureScene): target = Plane() # target.match_height(radar_dish) target.next_to(radar_dish, RIGHT, buff = LARGE_BUFF) - target_movement = AmbientMovement(target, direction = RIGHT, rate = 1.25) + target_movement = ContinualMovement(target, direction = RIGHT, rate = 1.25) pulse = RadarPulse(radar_dish, target) @@ -1730,7 +1730,7 @@ class MentionDopplerRadar(TeacherStudentsScene): plane = Plane() plane.to_edge(RIGHT) plane.align_to(dish) - plane_flight = AmbientMovement( + plane_flight = ContinualMovement( plane, direction = LEFT, rate = 1, @@ -1911,7 +1911,7 @@ class IntroduceDopplerRadar(Scene): ShowCreation(sum_graph, run_time = 8, rate_func = None) ) pulse = RadarPulse(dish, plane, n_pulse_singletons = 12) - plane_flight = AmbientMovement( + plane_flight = ContinualMovement( plane, direction = LEFT, rate = 1.5 ) @@ -2690,7 +2690,7 @@ class AmbiguityInLongEchos(IntroduceDopplerRadar, PiCreatureScene): object_velocities = self.object_velocities movements = self.object_movements = [ - AmbientMovement( + ContinualMovement( obj, direction = v/np.linalg.norm(v), rate = np.linalg.norm(v) @@ -3366,7 +3366,7 @@ class SortOfDopplerEffect(PiCreatureScene): t_tracker = VectorizedPoint() #x-coordinate gives wave number k_tracker = VectorizedPoint(2*RIGHT) - tk_movement = AmbientMovement(t_tracker, direction = RIGHT, rate = 1) + tk_movement = ContinualMovement(t_tracker, direction = RIGHT, rate = 1) def get_wave(): t = t_tracker.get_center()[0] k = k_tracker.get_center()[0] @@ -3399,7 +3399,7 @@ class SortOfDopplerEffect(PiCreatureScene): rect = ScreenRectangle(height = 2) rect.to_edge(RIGHT) - rect_movement = AmbientMovement(rect, direction = LEFT, rate = 1) + rect_movement = ContinualMovement(rect, direction = LEFT, rate = 1) randy = self.pi_creature randy_look_at = ContinualUpdateFromFunc( @@ -3523,7 +3523,7 @@ class HangingWeightsScene(MovingCameraScene): k_tracker = self.k_tracker = VectorizedPoint() t_tracker = self.t_tracker = VectorizedPoint() - self.t_tracker_walk = AmbientMovement(t_tracker, direction = RIGHT, rate = 1) + self.t_tracker_walk = ContinualMovement(t_tracker, direction = RIGHT, rate = 1) equilibrium_height = springs.get_height() def update_springs(springs): for spring in springs: @@ -3637,7 +3637,7 @@ class HangingWeightsScene(MovingCameraScene): def moving_reference_frame(self): rect = ScreenRectangle(height = 2.1*FRAME_Y_RADIUS) - rect_movement = AmbientMovement(rect, direction = LEFT, rate = 2) + rect_movement = ContinualMovement(rect, direction = LEFT, rate = 2) camera_frame = self.camera_frame self.add(rect) @@ -4371,7 +4371,7 @@ class ThinkOfHeisenbergUncertainty(PiCreatureScene): self.add() freq = 1 continual_anims = [ - AmbientMovement(time_tracker, direction = RIGHT, rate = 1), + ContinualMovement(time_tracker, direction = RIGHT, rate = 1), ContinualUpdateFromFunc( dot_gdw, lambda d : d.scale_to_fit_width( diff --git a/topics/three_dimensions.py b/topics/three_dimensions.py index 37916087..54bd0cb9 100644 --- a/topics/three_dimensions.py +++ b/topics/three_dimensions.py @@ -1,7 +1,7 @@ from constants import * -from continual_animation.continual_animation import AmbientMovement +from continual_animation.continual_animation import ContinualMovement from animation.transform import ApplyMethod from camera.camera import Camera from mobject.vectorized_mobject import VGroup @@ -184,7 +184,7 @@ class ThreeDScene(Scene): self.camera.set_position(phi, theta, distance, center_x, center_y, center_z) def begin_ambient_camera_rotation(self, rate = 0.01): - self.ambient_camera_rotation = AmbientMovement( + self.ambient_camera_rotation = ContinualMovement( self.camera.rotation_mobject, direction = UP, rate = rate From b757f8b422c50690c4a516bd75780c4cc264c1d8 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Sat, 31 Mar 2018 16:07:24 -0700 Subject: [PATCH 04/17] Pulled pi_creature constructs out into their own folder, so as to note some distinction from the remainder of the library --- big_ol_pile_of_manim_imports.py | 5 +- pi_creature/__init__.py | 0 pi_creature/pi_creature.py | 316 +++++++++++ pi_creature/pi_creature_animations.py | 96 ++++ pi_creature/pi_creature_scene.py | 362 ++++++++++++ topics/characters.py | 762 -------------------------- topics/common_scenes.py | 1 - topics/complex_numbers.py | 1 - topics/fractals.py | 16 - topics/geometry.py | 4 - topics/graph_theory.py | 5 - topics/matrix.py | 3 - 12 files changed, 778 insertions(+), 793 deletions(-) create mode 100644 pi_creature/__init__.py create mode 100644 pi_creature/pi_creature.py create mode 100644 pi_creature/pi_creature_animations.py create mode 100644 pi_creature/pi_creature_scene.py delete mode 100644 topics/characters.py diff --git a/big_ol_pile_of_manim_imports.py b/big_ol_pile_of_manim_imports.py index 0977cc5d..58361f9d 100644 --- a/big_ol_pile_of_manim_imports.py +++ b/big_ol_pile_of_manim_imports.py @@ -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 * diff --git a/pi_creature/__init__.py b/pi_creature/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pi_creature/pi_creature.py b/pi_creature/pi_creature.py new file mode 100644 index 00000000..479c542d --- /dev/null +++ b/pi_creature/pi_creature.py @@ -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) + + diff --git a/pi_creature/pi_creature_animations.py b/pi_creature/pi_creature_animations.py new file mode 100644 index 00000000..0a21ec39 --- /dev/null +++ b/pi_creature/pi_creature_animations.py @@ -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) diff --git a/pi_creature/pi_creature_scene.py b/pi_creature/pi_creature_scene.py new file mode 100644 index 00000000..b8f46a42 --- /dev/null +++ b/pi_creature/pi_creature_scene.py @@ -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, + ) + + diff --git a/topics/characters.py b/topics/characters.py deleted file mode 100644 index ffcbd668..00000000 --- a/topics/characters.py +++ /dev/null @@ -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, - ) - - - - - - - - - - - diff --git a/topics/common_scenes.py b/topics/common_scenes.py index 34a8d89b..9b2d208b 100644 --- a/topics/common_scenes.py +++ b/topics/common_scenes.py @@ -20,7 +20,6 @@ from topics.geometry import Rectangle from topics.geometry import Square from topics.objects import PatreonLogo - class OpeningQuote(Scene): CONFIG = { "quote" : [], diff --git a/topics/complex_numbers.py b/topics/complex_numbers.py index 437682d4..df566378 100644 --- a/topics/complex_numbers.py +++ b/topics/complex_numbers.py @@ -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" : {}, diff --git a/topics/fractals.py b/topics/fractals.py index 1e405a67..f3122dde 100644 --- a/topics/fractals.py +++ b/topics/fractals.py @@ -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" diff --git a/topics/geometry.py b/topics/geometry.py index 8be42047..c01899cf 100644 --- a/topics/geometry.py +++ b/topics/geometry.py @@ -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, diff --git a/topics/graph_theory.py b/topics/graph_theory.py index f3a7f6a1..0e9ae25d 100644 --- a/topics/graph_theory.py +++ b/topics/graph_theory.py @@ -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(),), diff --git a/topics/matrix.py b/topics/matrix.py index 936039f9..74630598 100644 --- a/topics/matrix.py +++ b/topics/matrix.py @@ -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]], From b9edafa9421d20d32c130b6070c06ed7c62fbac0 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Sat, 31 Mar 2018 16:28:02 -0700 Subject: [PATCH 05/17] Moved many folders from topics/ to the new once_useful_constructs/ --- active_projects/eop/bayes.py | 31 +---------------- active_projects/eop/bayes_footnote.py | 31 +---------------- active_projects/eop/combinations.py | 34 +------------------ active_projects/eop/independence.py | 31 +---------------- big_ol_pile_of_manim_imports.py | 15 ++++---- old_projects/basel/basel.py | 2 +- old_projects/basel/basel2.py | 2 +- old_projects/nn/playground.py | 29 +--------------- once_useful_constructs/NOTE.md | 1 + once_useful_constructs/__init__.py | 0 .../arithmetic.py | 0 .../combinatorics.py | 0 .../counting.py | 1 - .../fractals.py | 9 ++--- .../graph_theory.py | 0 .../basel => once_useful_constructs}/light.py | 0 topics/common_scenes.py | 20 ++--------- topics/objects.py | 2 +- topics/probability.py | 3 +- 19 files changed, 25 insertions(+), 186 deletions(-) create mode 100644 once_useful_constructs/NOTE.md create mode 100644 once_useful_constructs/__init__.py rename {topics => once_useful_constructs}/arithmetic.py (100%) rename {topics => once_useful_constructs}/combinatorics.py (100%) rename {topics => once_useful_constructs}/counting.py (99%) rename {topics => once_useful_constructs}/fractals.py (99%) rename {topics => once_useful_constructs}/graph_theory.py (100%) rename {old_projects/basel => once_useful_constructs}/light.py (100%) diff --git a/active_projects/eop/bayes.py b/active_projects/eop/bayes.py index 1ad2d9bd..39b63e58 100644 --- a/active_projects/eop/bayes.py +++ b/active_projects/eop/bayes.py @@ -1,33 +1,4 @@ -from constants import * - -from mobject.tex_mobject import TexMobject -from mobject.mobject import Mobject -from mobject.image_mobject import ImageMobject -from mobject.vectorized_mobject import * - -from animation.animation import Animation -from animation.transform import * -from animation.simple_animations import * -from animation.composition import * -from animation.playground import * -from topics.geometry import * -from topics.characters import * -from topics.functions import * -from topics.fractals import * -from topics.number_line import * -from topics.combinatorics import * -from topics.numerals import * -from topics.three_dimensions import * -from topics.objects import * -from topics.complex_numbers import * -from topics.common_scenes import * -from topics.probability import * -from scene.scene import Scene -from scene.reconfigurable_scene import ReconfigurableScene -from scene.zoomed_scene import * -from camera.camera import Camera -from mobject.svg_mobject import * -from mobject.tex_mobject import * +from big_ol_pile_of_manim_imports import * #revert_to_original_skipping_status diff --git a/active_projects/eop/bayes_footnote.py b/active_projects/eop/bayes_footnote.py index 38bacda7..06f4b2b3 100644 --- a/active_projects/eop/bayes_footnote.py +++ b/active_projects/eop/bayes_footnote.py @@ -1,33 +1,4 @@ -from constants import * - -from mobject.tex_mobject import TexMobject -from mobject.mobject import Mobject -from mobject.image_mobject import ImageMobject -from mobject.vectorized_mobject import * - -from animation.animation import Animation -from animation.transform import * -from animation.simple_animations import * -from animation.composition import * -from animation.playground import * -from topics.geometry import * -from topics.characters import * -from topics.functions import * -from topics.fractals import * -from topics.number_line import * -from topics.combinatorics import * -from topics.numerals import * -from topics.three_dimensions import * -from topics.objects import * -from topics.complex_numbers import * -from topics.common_scenes import * -from topics.probability import * -from scene.scene import Scene -from scene.reconfigurable_scene import ReconfigurableScene -from scene.zoomed_scene import * -from camera.camera import Camera -from mobject.svg_mobject import * -from mobject.tex_mobject import * +from big_ol_pile_of_manim_imports import * from eop.bayes import IntroducePokerHand diff --git a/active_projects/eop/combinations.py b/active_projects/eop/combinations.py index 906fa96f..31e88ce9 100644 --- a/active_projects/eop/combinations.py +++ b/active_projects/eop/combinations.py @@ -1,36 +1,4 @@ -from constants import * - -from mobject.tex_mobject import TexMobject -from mobject.mobject import Mobject -from mobject.image_mobject import ImageMobject -from mobject.vectorized_mobject import * - -from animation.animation import Animation -from animation.transform import * -from animation.simple_animations import * -from animation.composition import * -from animation.playground import * -from animation.continual_animation import * -from topics.geometry import * -from topics.characters import * -from topics.functions import * -from topics.fractals import * -from topics.number_line import * -from topics.combinatorics import * -from topics.numerals import * -from topics.three_dimensions import * -from topics.objects import * -from topics.probability import * -from topics.complex_numbers import * -from scene.scene import Scene -from scene.reconfigurable_scene import ReconfigurableScene -from scene.zoomed_scene import * -from camera.camera import Camera -from mobject.svg_mobject import * -from mobject.tex_mobject import * -from topics.graph_scene import * -from topics.probability import * -from topics.common_scenes import * +from big_ol_pile_of_manim_imports import * #revert_to_original_skipping_status diff --git a/active_projects/eop/independence.py b/active_projects/eop/independence.py index d31b65b3..48fd868b 100644 --- a/active_projects/eop/independence.py +++ b/active_projects/eop/independence.py @@ -1,33 +1,4 @@ -from constants import * - -from mobject.tex_mobject import TexMobject -from mobject.mobject import Mobject -from mobject.image_mobject import ImageMobject -from mobject.vectorized_mobject import * - -from animation.animation import Animation -from animation.transform import * -from animation.simple_animations import * -from animation.composition import * -from animation.playground import * -from topics.geometry import * -from topics.characters import * -from topics.functions import * -from topics.fractals import * -from topics.number_line import * -from topics.combinatorics import * -from topics.numerals import * -from topics.three_dimensions import * -from topics.objects import * -from topics.complex_numbers import * -from topics.common_scenes import * -from topics.probability import * -from scene.scene import Scene -from scene.reconfigurable_scene import ReconfigurableScene -from scene.zoomed_scene import * -from camera.camera import Camera -from mobject.svg_mobject import * -from mobject.tex_mobject import * +from big_ol_pile_of_manim_imports import * from scene.scene import ProgressDisplay import scipy diff --git a/big_ol_pile_of_manim_imports.py b/big_ol_pile_of_manim_imports.py index 58361f9d..0a033ec9 100644 --- a/big_ol_pile_of_manim_imports.py +++ b/big_ol_pile_of_manim_imports.py @@ -25,8 +25,8 @@ from animation.transform import * from animation.update import * from camera.camera import * -from camera.moving_camera import * from camera.mapping_camera import * +from camera.moving_camera import * from continual_animation.continual_animation import * from continual_animation.from_animation import * @@ -49,16 +49,18 @@ from scene.scene import * from scene.scene_from_video import * from scene.zoomed_scene import * -from topics.arithmetic import * -from topics.combinatorics import * +from once_useful_constructs.arithmetic import * +from once_useful_constructs.combinatorics import * +from once_useful_constructs.counting import * +from once_useful_constructs.fractals import * +from once_useful_constructs.graph_theory import * +from once_useful_constructs.light import * + from topics.common_scenes import * from topics.complex_numbers import * -from topics.counting import * -from topics.fractals import * from topics.functions import * from topics.geometry import * from topics.graph_scene import * -from topics.graph_theory import * from topics.matrix import * from topics.number_line import * from topics.numerals import * @@ -89,6 +91,7 @@ import os import random import re import string +import sys from PIL import Image from colour import Color diff --git a/old_projects/basel/basel.py b/old_projects/basel/basel.py index 2937ced5..259c537c 100644 --- a/old_projects/basel/basel.py +++ b/old_projects/basel/basel.py @@ -2,7 +2,7 @@ from big_ol_pile_of_manim_imports import * -from old_projects.basel.light import * +from once_useful_constructs.light import * import types import functools diff --git a/old_projects/basel/basel2.py b/old_projects/basel/basel2.py index 55e8cd72..1638a26f 100644 --- a/old_projects/basel/basel2.py +++ b/old_projects/basel/basel2.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- from big_ol_pile_of_manim_imports import * -from old_projects.basel.light import * +from once_useful_constructs.light import * import types import functools diff --git a/old_projects/nn/playground.py b/old_projects/nn/playground.py index 478faeb4..1edcbf7f 100644 --- a/old_projects/nn/playground.py +++ b/old_projects/nn/playground.py @@ -7,34 +7,7 @@ import os.path sys.path.append(os.path.join(os.path.dirname(__file__), '..')) from constants import * -from mobject.tex_mobject import TexMobject -from mobject.mobject import Mobject, Group -from mobject.image_mobject import ImageMobject -from mobject.vectorized_mobject import * - -from animation.animation import Animation -from animation.transform import * -from animation.simple_animations import * -from animation.composition import * -from animation.playground import * -from animation.continual_animation import * -from topics.geometry import * -from topics.characters import * -from topics.functions import * -from topics.fractals import * -from topics.number_line import * -from topics.combinatorics import * -from topics.numerals import * -from topics.three_dimensions import * -from topics.objects import * -from topics.probability import * -from topics.complex_numbers import * -from scene.scene import Scene -from scene.reconfigurable_scene import ReconfigurableScene -from scene.zoomed_scene import * -from camera.camera import Camera -from mobject.svg_mobject import * -from mobject.tex_mobject import * +from big_ol_pile_of_manim_imports import * from nn.network import * from nn.part1 import * diff --git a/once_useful_constructs/NOTE.md b/once_useful_constructs/NOTE.md new file mode 100644 index 00000000..931ae0de --- /dev/null +++ b/once_useful_constructs/NOTE.md @@ -0,0 +1 @@ +This folder contains a collection of various things that were built for a video at some point, but were really one-off and should be given more careful consideration before being brought into the main library. In particular, there is really no guarantee of these being fully functional. \ No newline at end of file diff --git a/once_useful_constructs/__init__.py b/once_useful_constructs/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/topics/arithmetic.py b/once_useful_constructs/arithmetic.py similarity index 100% rename from topics/arithmetic.py rename to once_useful_constructs/arithmetic.py diff --git a/topics/combinatorics.py b/once_useful_constructs/combinatorics.py similarity index 100% rename from topics/combinatorics.py rename to once_useful_constructs/combinatorics.py diff --git a/topics/counting.py b/once_useful_constructs/counting.py similarity index 99% rename from topics/counting.py rename to once_useful_constructs/counting.py index e8aa6dbb..8b43ae5f 100644 --- a/topics/counting.py +++ b/once_useful_constructs/counting.py @@ -13,7 +13,6 @@ from animation.transform import Transform from topics.geometry import Arrow from topics.geometry import Circle from topics.geometry import Dot -# from topics.fractals import * from scene.scene import Scene diff --git a/topics/fractals.py b/once_useful_constructs/fractals.py similarity index 99% rename from topics/fractals.py rename to once_useful_constructs/fractals.py index f3122dde..5f2e7200 100644 --- a/topics/fractals.py +++ b/once_useful_constructs/fractals.py @@ -1,8 +1,8 @@ from animation.creation import ShowCreation from animation.transform import Transform -from characters import PiCreature -from characters import Randolph -from characters import get_all_pi_creature_modes +from pi_creature.pi_creature import PiCreature +from pi_creature.pi_creature import Randolph +from pi_creature.pi_creature import get_all_pi_creature_modes from mobject.vectorized_mobject import VGroup from mobject.vectorized_mobject import VMobject from mobject.vectorized_mobject import VectorizedPoint @@ -22,7 +22,6 @@ from utils.space_ops import rotation_matrix from constants import * - def rotate(points, angle = np.pi, axis = OUT): if axis is None: return points @@ -277,8 +276,6 @@ class CircularFractal(SelfSimilarFractal): part.rotate(i*2*np.pi/self.num_subparts, about_point = ORIGIN) self.num_subparts -= 1 - - ######## Space filling curves ############ class JaggedCurvePiece(VMobject): diff --git a/topics/graph_theory.py b/once_useful_constructs/graph_theory.py similarity index 100% rename from topics/graph_theory.py rename to once_useful_constructs/graph_theory.py diff --git a/old_projects/basel/light.py b/once_useful_constructs/light.py similarity index 100% rename from old_projects/basel/light.py rename to once_useful_constructs/light.py diff --git a/topics/common_scenes.py b/topics/common_scenes.py index 9b2d208b..bc3b88b3 100644 --- a/topics/common_scenes.py +++ b/topics/common_scenes.py @@ -12,9 +12,9 @@ from mobject.tex_mobject import TexMobject from mobject.tex_mobject import TextMobject 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 pi_creature.pi_creature_animations import Blink +from pi_creature.pi_creature import Mortimer +from pi_creature.pi_creature import Randolph from topics.geometry import DashedLine from topics.geometry import Rectangle from topics.geometry import Square @@ -209,17 +209,3 @@ class TODOStub(Scene): self.add(TextMobject("TODO: %s"%self.message)) self.wait() - - - - - - - - - - - - - - diff --git a/topics/objects.py b/topics/objects.py index 48a999e2..ee0d4deb 100644 --- a/topics/objects.py +++ b/topics/objects.py @@ -518,7 +518,7 @@ class Car(SVGMobject): self.set_stroke(color = WHITE, width = 0) self.set_fill(self.color, opacity = 1) - from topics.characters import Randolph + from pi_creature.pi_creature import Randolph randy = Randolph(mode = "happy") randy.scale_to_fit_height(0.6*self.get_height()) randy.stretch(0.8, 0) diff --git a/topics/probability.py b/topics/probability.py index 8e086fb8..3034859a 100644 --- a/topics/probability.py +++ b/topics/probability.py @@ -382,7 +382,6 @@ class BarChart(VGroup): def copy(self): return self.deepcopy() - ### Cards ### class DeckOfCards(VGroup): @@ -542,7 +541,7 @@ class PlayingCard(VGroup): return design def get_face_card_design(self, value, symbol): - from topics.characters import PiCreature + from pi_creature.pi_creature import PiCreature sub_rect = Rectangle( stroke_color = BLACK, fill_opacity = 0, From accef010400df962c31b299ad1d077a4b4458bf3 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Sat, 31 Mar 2018 16:32:34 -0700 Subject: [PATCH 06/17] Poor mans merge --- topics/geometry.py | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/topics/geometry.py b/topics/geometry.py index c01899cf..a8792ecd 100644 --- a/topics/geometry.py +++ b/topics/geometry.py @@ -484,7 +484,7 @@ class Arrow(Line): self.init_colors() def init_tip(self): - self.tip = self.add_tip() + self.add_tip() def add_tip(self, add_at_end = True): tip = VMobject( @@ -497,13 +497,16 @@ class Arrow(Line): ) self.set_tip_points(tip, add_at_end, preserve_normal = False) self.add(tip) + if not hasattr(self, 'tip'): + self.tip = [] + self.tip.append(tuple((tip, add_at_end))) return tip def add_rectangular_stem(self): self.rect = Rectangle( stroke_width = 0, - fill_color = self.tip.get_fill_color(), - fill_opacity = self.tip.get_fill_opacity() + fill_color = self.tip[0][0].get_fill_color(), + fill_opacity = self.tip[0][0].get_fill_opacity() ) self.add_to_back(self.rect) self.set_stroke(width = 0) @@ -512,7 +515,7 @@ class Arrow(Line): def set_rectangular_stem_points(self): start, end = self.get_start_and_end() vect = end - start - tip_base_points = self.tip.get_anchors()[1:] + tip_base_points = self.tip[0][0].get_anchors()[1:] tip_base = center_of_mass(tip_base_points) tbp1, tbp2 = tip_base_points perp_vect = tbp2 - tbp1 @@ -536,8 +539,8 @@ class Arrow(Line): return self def set_tip_points( - self, tip, - add_at_end = True, + self, tip, + add_at_end = True, tip_length = None, preserve_normal = True, ): @@ -565,7 +568,7 @@ class Arrow(Line): v *= tip_length/np.linalg.norm(v) ratio = self.tip_width_to_length_ratio tip.set_points_as_corners([ - end_point, + end_point, end_point-vect+perp_vect*ratio/2, end_point-vect-perp_vect*ratio/2, ]) @@ -573,7 +576,7 @@ class Arrow(Line): return self def get_normal_vector(self): - p0, p1, p2 = self.tip.get_anchors() + p0, p1, p2 = self.tip[0][0].get_anchors() result = np.cross(p2 - p1, p1 - p0) norm = np.linalg.norm(result) if norm == 0: @@ -587,7 +590,7 @@ class Arrow(Line): def get_end(self): if hasattr(self, "tip"): - return self.tip.get_anchors()[0] + return self.tip[0][0].get_anchors()[0] else: return Line.get_end(self) @@ -596,14 +599,16 @@ class Arrow(Line): def put_start_and_end_on(self, *args, **kwargs): Line.put_start_and_end_on(self, *args, **kwargs) - self.set_tip_points(self.tip, preserve_normal = False) + self.set_tip_points(self.tip[0][0], preserve_normal = False) self.set_rectangular_stem_points() return self def scale(self, scale_factor, **kwargs): Line.scale(self, scale_factor, **kwargs) if self.preserve_tip_size_when_scaling: - self.set_tip_points(self.tip) + for t in self.tip: + print(t) + self.set_tip_points(t[0], add_at_end=t[1]) if self.use_rectangular_stem: self.set_rectangular_stem_points() return self @@ -623,8 +628,7 @@ class Vector(Arrow): class DoubleArrow(Arrow): def init_tip(self): - self.tip = self.add_tip() - self.second_tip = self.add_tip(add_at_end = False) + self.tip = [(self.add_tip(), True), (self.add_tip(add_at_end = False), False)] class CubicBezier(VMobject): def __init__(self, points, **kwargs): From fc3dc6480500b4cf58ff7db701ceb2ec4b3dca6c Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Sat, 31 Mar 2018 18:05:02 -0700 Subject: [PATCH 07/17] Refactor many definitions out of topics and into folders within mobject --- animation/creation.py | 6 +- animation/indication.py | 6 +- animation/specialized.py | 67 ++++++++ big_ol_pile_of_manim_imports.py | 18 +- camera/camera.py | 6 +- mobject/frame.py | 44 +++++ {topics => mobject}/geometry.py | 109 ++---------- mobject/shape_matchers.py | 56 +++++++ mobject/svg/__init__.py | 0 mobject/svg/brace.py | 130 +++++++++++++++ topics/objects.py => mobject/svg/drawings.py | 156 ++---------------- mobject/{ => svg}/svg_mobject.py | 8 +- mobject/{ => svg}/tex_mobject.py | 70 +------- mobject/types/__init__.py | 0 mobject/{ => types}/image_mobject.py | 1 - mobject/{ => types}/point_cloud_mobject.py | 0 mobject/{ => types}/vectorized_mobject.py | 18 ++ mobject/value_tracker.py | 43 +++++ old_projects/uncertainty.py | 34 ---- once_useful_constructs/arithmetic.py | 2 +- once_useful_constructs/combinatorics.py | 4 +- once_useful_constructs/counting.py | 14 +- once_useful_constructs/fractals.py | 16 +- once_useful_constructs/light.py | 32 +++- {mobject => once_useful_constructs}/region.py | 0 pi_creature/pi_creature.py | 10 +- pi_creature/pi_creature_animations.py | 2 +- pi_creature/pi_creature_scene.py | 8 +- scene/moving_camera_scene.py | 2 +- scene/scene.py | 2 +- scene/zoomed_scene.py | 2 +- topics/common_scenes.py | 14 +- topics/complex_numbers.py | 6 +- topics/functions.py | 2 +- topics/graph_scene.py | 16 +- topics/matrix.py | 20 +-- topics/number_line.py | 10 +- topics/numerals.py | 10 +- topics/probability.py | 26 +-- topics/three_dimensions.py | 10 +- topics/vector_space_scene.py | 22 +-- utils/space_ops.py | 9 - 42 files changed, 547 insertions(+), 464 deletions(-) create mode 100644 animation/specialized.py create mode 100644 mobject/frame.py rename {topics => mobject}/geometry.py (88%) create mode 100644 mobject/shape_matchers.py create mode 100644 mobject/svg/__init__.py create mode 100644 mobject/svg/brace.py rename topics/objects.py => mobject/svg/drawings.py (79%) rename mobject/{ => svg}/svg_mobject.py (98%) rename mobject/{ => svg}/tex_mobject.py (82%) create mode 100644 mobject/types/__init__.py rename mobject/{ => types}/image_mobject.py (98%) rename mobject/{ => types}/point_cloud_mobject.py (100%) rename mobject/{ => types}/vectorized_mobject.py (96%) create mode 100644 mobject/value_tracker.py rename {mobject => once_useful_constructs}/region.py (100%) diff --git a/animation/creation.py b/animation/creation.py index 8ce90f0d..e345f893 100644 --- a/animation/creation.py +++ b/animation/creation.py @@ -5,9 +5,9 @@ import numpy as np from constants import * from animation.animation import Animation -from mobject.tex_mobject import TextMobject -from mobject.vectorized_mobject import VMobject -from mobject.vectorized_mobject import VectorizedPoint +from mobject.svg.tex_mobject import TextMobject +from mobject.types.vectorized_mobject import VMobject +from mobject.types.vectorized_mobject import VectorizedPoint from animation.transform import Transform from utils.bezier import interpolate from utils.config_ops import digest_config diff --git a/animation/indication.py b/animation/indication.py index 67531863..0a4ad091 100644 --- a/animation/indication.py +++ b/animation/indication.py @@ -10,9 +10,9 @@ from animation.creation import ShowPartial from animation.transform import Transform from mobject.mobject import Group from mobject.mobject import Mobject -from mobject.vectorized_mobject import VMobject -from topics.geometry import Circle -from topics.geometry import Dot +from mobject.types.vectorized_mobject import VMobject +from mobject.geometry import Circle +from mobject.geometry import Dot from utils.config_ops import digest_config from utils.rate_functions import squish_rate_func from utils.rate_functions import there_and_back diff --git a/animation/specialized.py b/animation/specialized.py new file mode 100644 index 00000000..c12f85ab --- /dev/null +++ b/animation/specialized.py @@ -0,0 +1,67 @@ +from __future__ import absolute_import + +import numpy as np + +from constants import * + +from animation.transform import ApplyMethod +from animation.composition import LaggedStart +from mobject.svg.drawings import Car +from mobject.types.vectorized_mobject import VGroup +from mobject.geometry import Circle +from utils.config_ops import digest_config + +class MoveCar(ApplyMethod): + CONFIG = { + "moving_forward" : True, + } + def __init__(self, car, target_point, **kwargs): + assert isinstance(car, Car) + ApplyMethod.__init__(self, car.move_to, target_point, **kwargs) + displacement = self.target_mobject.get_right()-self.starting_mobject.get_right() + distance = np.linalg.norm(displacement) + if not self.moving_forward: + distance *= -1 + tire_radius = car.get_tires()[0].get_width()/2 + self.total_tire_radians = -distance/tire_radius + + def update_mobject(self, alpha): + ApplyMethod.update_mobject(self, alpha) + if alpha == 0: + return + radians = alpha*self.total_tire_radians + for tire in self.mobject.get_tires(): + tire.rotate_in_place(radians) + +class Broadcast(LaggedStart): + CONFIG = { + "small_radius" : 0.0, + "big_radius" : 5, + "n_circles" : 5, + "start_stroke_width" : 8, + "color" : WHITE, + "remover" : True, + "lag_ratio" : 0.7, + "run_time" : 3, + "remover" : True, + } + def __init__(self, focal_point, **kwargs): + digest_config(self, kwargs) + circles = VGroup() + for x in range(self.n_circles): + circle = Circle( + radius = self.big_radius, + stroke_color = BLACK, + stroke_width = 0, + ) + circle.move_to(focal_point) + circle.save_state() + circle.scale_to_fit_width(self.small_radius*2) + circle.set_stroke(self.color, self.start_stroke_width) + circles.add(circle) + LaggedStart.__init__( + self, ApplyMethod, circles, + lambda c : (c.restore,), + **kwargs + ) + diff --git a/big_ol_pile_of_manim_imports.py b/big_ol_pile_of_manim_imports.py index 0a033ec9..0dbd9e73 100644 --- a/big_ol_pile_of_manim_imports.py +++ b/big_ol_pile_of_manim_imports.py @@ -21,6 +21,7 @@ from animation.creation import * from animation.indication import * from animation.movement import * from animation.rotation import * +from animation.specialized import * from animation.transform import * from animation.update import * @@ -32,12 +33,17 @@ from continual_animation.continual_animation import * from continual_animation.from_animation import * from continual_animation.update import * -from mobject.image_mobject import * from mobject.mobject import * -from mobject.point_cloud_mobject import * -from mobject.svg_mobject import * -from mobject.tex_mobject import * -from mobject.vectorized_mobject import * +from mobject.frame import * +from mobject.geometry import * +from mobject.shape_matchers import * +from mobject.value_tracker import * +from mobject.svg.brace import * +from mobject.svg.svg_mobject import * +from mobject.svg.tex_mobject import * +from mobject.types.image_mobject import * +from mobject.types.point_cloud_mobject import * +from mobject.types.vectorized_mobject import * from pi_creature.pi_creature import * from pi_creature.pi_creature_animations import * @@ -59,12 +65,10 @@ from once_useful_constructs.light import * from topics.common_scenes import * from topics.complex_numbers import * from topics.functions import * -from topics.geometry import * from topics.graph_scene import * from topics.matrix import * from topics.number_line import * from topics.numerals import * -from topics.objects import * from topics.probability import * from topics.three_dimensions import * from topics.vector_space_scene import * diff --git a/camera/camera.py b/camera/camera.py index aba89340..c9f4dab0 100644 --- a/camera/camera.py +++ b/camera/camera.py @@ -10,10 +10,10 @@ from PIL import Image from colour import Color from constants import * -from mobject.image_mobject import ImageMobject +from mobject.types.image_mobject import ImageMobject from mobject.mobject import Mobject -from mobject.point_cloud_mobject import PMobject -from mobject.vectorized_mobject import VMobject +from mobject.types.point_cloud_mobject import PMobject +from mobject.types.vectorized_mobject import VMobject from utils.color import color_to_int_rgba from utils.color import rgb_to_hex from utils.config_ops import digest_config diff --git a/mobject/frame.py b/mobject/frame.py new file mode 100644 index 00000000..0ca977a6 --- /dev/null +++ b/mobject/frame.py @@ -0,0 +1,44 @@ +from __future__ import absolute_import + +from constants import * +from mobject.geometry import Rectangle +from utils.config_ops import digest_config + +class ScreenRectangle(Rectangle): + CONFIG = { + "width_to_height_ratio" : 16.0/9.0, + "height" : 4, + } + def generate_points(self): + self.width = self.width_to_height_ratio * self.height + Rectangle.generate_points(self) + +class FullScreenRectangle(ScreenRectangle): + CONFIG = { + "height" : FRAME_HEIGHT, + } + +class FullScreenFadeRectangle(FullScreenRectangle): + CONFIG = { + "stroke_width" : 0, + "fill_color" : BLACK, + "fill_opacity" : 0.7, + } + +class PictureInPictureFrame(Rectangle): + CONFIG = { + "height" : 3, + "aspect_ratio" : (16, 9) + } + def __init__(self, **kwargs): + digest_config(self, kwargs) + height = self.height + if "height" in kwargs: + kwargs.pop("height") + Rectangle.__init__( + self, + width = self.aspect_ratio[0], + height = self.aspect_ratio[1], + **kwargs + ) + self.scale_to_fit_height(height) diff --git a/topics/geometry.py b/mobject/geometry.py similarity index 88% rename from topics/geometry.py rename to mobject/geometry.py index a8792ecd..a2182d5b 100644 --- a/topics/geometry.py +++ b/mobject/geometry.py @@ -1,11 +1,13 @@ +from __future__ import absolute_import + from constants import * import itertools as it import numpy as np from mobject.mobject import Mobject -from mobject.vectorized_mobject import VGroup -from mobject.vectorized_mobject import VMobject +from mobject.types.vectorized_mobject import VGroup +from mobject.types.vectorized_mobject import VMobject from utils.bezier import interpolate from utils.config_ops import digest_config from utils.config_ops import digest_locals @@ -495,18 +497,20 @@ class Arrow(Line): stroke_color = self.color, stroke_width = 0, ) + tip.add_at_end = add_at_end self.set_tip_points(tip, add_at_end, preserve_normal = False) self.add(tip) if not hasattr(self, 'tip'): - self.tip = [] - self.tip.append(tuple((tip, add_at_end))) + self.tip = VGroup() + self.tip.match_style(tip) + self.tip.add(tip) return tip def add_rectangular_stem(self): self.rect = Rectangle( stroke_width = 0, - fill_color = self.tip[0][0].get_fill_color(), - fill_opacity = self.tip[0][0].get_fill_opacity() + fill_color = self.tip.get_fill_color(), + fill_opacity = self.tip.get_fill_opacity() ) self.add_to_back(self.rect) self.set_stroke(width = 0) @@ -515,7 +519,7 @@ class Arrow(Line): def set_rectangular_stem_points(self): start, end = self.get_start_and_end() vect = end - start - tip_base_points = self.tip[0][0].get_anchors()[1:] + tip_base_points = self.tip[0].get_anchors()[1:] tip_base = center_of_mass(tip_base_points) tbp1, tbp2 = tip_base_points perp_vect = tbp2 - tbp1 @@ -576,7 +580,7 @@ class Arrow(Line): return self def get_normal_vector(self): - p0, p1, p2 = self.tip[0][0].get_anchors() + p0, p1, p2 = self.tip[0].get_anchors() result = np.cross(p2 - p1, p1 - p0) norm = np.linalg.norm(result) if norm == 0: @@ -590,7 +594,7 @@ class Arrow(Line): def get_end(self): if hasattr(self, "tip"): - return self.tip[0][0].get_anchors()[0] + return self.tip[0].get_anchors()[0] else: return Line.get_end(self) @@ -599,7 +603,7 @@ class Arrow(Line): def put_start_and_end_on(self, *args, **kwargs): Line.put_start_and_end_on(self, *args, **kwargs) - self.set_tip_points(self.tip[0][0], preserve_normal = False) + self.set_tip_points(self.tip[0], preserve_normal = False) self.set_rectangular_stem_points() return self @@ -607,8 +611,7 @@ class Arrow(Line): Line.scale(self, scale_factor, **kwargs) if self.preserve_tip_size_when_scaling: for t in self.tip: - print(t) - self.set_tip_points(t[0], add_at_end=t[1]) + self.set_tip_points(t, add_at_end=t.add_at_end) if self.use_rectangular_stem: self.set_rectangular_stem_points() return self @@ -692,88 +695,6 @@ class Square(Rectangle): **kwargs ) -class SurroundingRectangle(Rectangle): - CONFIG = { - "color" : YELLOW, - "buff" : SMALL_BUFF, - } - def __init__(self, mobject, **kwargs): - digest_config(self, kwargs) - kwargs["width"] = mobject.get_width() + 2*self.buff - kwargs["height"] = mobject.get_height() + 2*self.buff - Rectangle.__init__(self, **kwargs) - self.move_to(mobject) - -class BackgroundRectangle(SurroundingRectangle): - CONFIG = { - "color" : BLACK, - "stroke_width" : 0, - "fill_opacity" : 0.75, - "buff" : 0 - } - def __init__(self, mobject, **kwargs): - SurroundingRectangle.__init__(self, mobject, **kwargs) - self.original_fill_opacity = self.fill_opacity - - def pointwise_become_partial(self, mobject, a, b): - self.set_fill(opacity = b*self.original_fill_opacity) - return self - - def get_fill_color(self): - return Color(self.color) - -class ScreenRectangle(Rectangle): - CONFIG = { - "width_to_height_ratio" : 16.0/9.0, - "height" : 4, - } - def generate_points(self): - self.width = self.width_to_height_ratio * self.height - Rectangle.generate_points(self) - -class FullScreenRectangle(ScreenRectangle): - CONFIG = { - "height" : FRAME_HEIGHT, - } - -class FullScreenFadeRectangle(FullScreenRectangle): - CONFIG = { - "stroke_width" : 0, - "fill_color" : BLACK, - "fill_opacity" : 0.7, - } - -class PictureInPictureFrame(Rectangle): - CONFIG = { - "height" : 3, - "aspect_ratio" : (16, 9) - } - def __init__(self, **kwargs): - digest_config(self, kwargs) - height = self.height - if "height" in kwargs: - kwargs.pop("height") - Rectangle.__init__( - self, - width = self.aspect_ratio[0], - height = self.aspect_ratio[1], - **kwargs - ) - self.scale_to_fit_height(height) - -class Cross(VGroup): - CONFIG = { - "stroke_color" : RED, - "stroke_width" : 6, - } - def __init__(self, mobject, **kwargs): - VGroup.__init__(self, - Line(UP+LEFT, DOWN+RIGHT), - Line(UP+RIGHT, DOWN+LEFT), - ) - self.replace(mobject, stretch = True) - self.set_stroke(self.stroke_color, self.stroke_width) - class Grid(VMobject): CONFIG = { "height" : 6.0, diff --git a/mobject/shape_matchers.py b/mobject/shape_matchers.py new file mode 100644 index 00000000..b2e1199a --- /dev/null +++ b/mobject/shape_matchers.py @@ -0,0 +1,56 @@ +from __future__ import absolute_import + +from constants import * + +from mobject.geometry import Rectangle +from mobject.geometry import Line +from mobject.types.vectorized_mobject import VGroup +from utils.config_ops import digest_config +from utils.color import Color + +class SurroundingRectangle(Rectangle): + CONFIG = { + "color" : YELLOW, + "buff" : SMALL_BUFF, + } + def __init__(self, mobject, **kwargs): + digest_config(self, kwargs) + kwargs["width"] = mobject.get_width() + 2*self.buff + kwargs["height"] = mobject.get_height() + 2*self.buff + Rectangle.__init__(self, **kwargs) + self.move_to(mobject) + +class BackgroundRectangle(SurroundingRectangle): + CONFIG = { + "color" : BLACK, + "stroke_width" : 0, + "fill_opacity" : 0.75, + "buff" : 0 + } + def __init__(self, mobject, **kwargs): + SurroundingRectangle.__init__(self, mobject, **kwargs) + self.original_fill_opacity = self.fill_opacity + + def pointwise_become_partial(self, mobject, a, b): + self.set_fill(opacity = b*self.original_fill_opacity) + return self + + def set_color(self): + # Can't be changin' me! + return self + + def get_fill_color(self): + return Color(self.color) + +class Cross(VGroup): + CONFIG = { + "stroke_color" : RED, + "stroke_width" : 6, + } + def __init__(self, mobject, **kwargs): + VGroup.__init__(self, + Line(UP+LEFT, DOWN+RIGHT), + Line(UP+RIGHT, DOWN+LEFT), + ) + self.replace(mobject, stretch = True) + self.set_stroke(self.stroke_color, self.stroke_width) diff --git a/mobject/svg/__init__.py b/mobject/svg/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/mobject/svg/brace.py b/mobject/svg/brace.py new file mode 100644 index 00000000..c56d882d --- /dev/null +++ b/mobject/svg/brace.py @@ -0,0 +1,130 @@ +from __future__ import absolute_import + +import numpy as np + +from constants import * + +from animation.composition import AnimationGroup +from animation.creation import FadeIn +from animation.creation import GrowFromCenter +from mobject.svg.tex_mobject import TexMobject +from mobject.svg.tex_mobject import TextMobject +from mobject.types.vectorized_mobject import VMobject +from utils.config_ops import digest_config + +class Brace(TexMobject): + CONFIG = { + "buff" : 0.2, + "width_multiplier" : 2, + "max_num_quads" : 15, + "min_num_quads" : 0, + } + def __init__(self, mobject, direction = DOWN, **kwargs): + digest_config(self, kwargs, locals()) + angle = -np.arctan2(*direction[:2]) + np.pi + mobject.rotate(-angle, about_point = ORIGIN) + left = mobject.get_corner(DOWN+LEFT) + right = mobject.get_corner(DOWN+RIGHT) + target_width = right[0]-left[0] + + ## Adding int(target_width) qquads gives approximately the right width + num_quads = np.clip( + int(self.width_multiplier*target_width), + self.min_num_quads, self.max_num_quads + ) + tex_string = "\\underbrace{%s}"%(num_quads*"\\qquad") + TexMobject.__init__(self, tex_string, **kwargs) + self.tip_point_index = np.argmin(self.get_all_points()[:,1]) + self.stretch_to_fit_width(target_width) + self.shift(left - self.get_corner(UP+LEFT) + self.buff*DOWN) + for mob in mobject, self: + mob.rotate(angle, about_point = ORIGIN) + + def put_at_tip(self, mob, use_next_to = True, **kwargs): + if use_next_to: + mob.next_to( + self.get_tip(), + np.round(self.get_direction()), + **kwargs + ) + else: + mob.move_to(self.get_tip()) + buff = kwargs.get("buff", DEFAULT_MOBJECT_TO_MOBJECT_BUFFER) + shift_distance = mob.get_width()/2.0+buff + mob.shift(self.get_direction()*shift_distance) + return self + + def get_text(self, *text, **kwargs): + text_mob = TextMobject(*text) + self.put_at_tip(text_mob, **kwargs) + return text_mob + + def get_tex(self, *tex, **kwargs): + tex_mob = TexMobject(*tex) + self.put_at_tip(tex_mob, **kwargs) + return tex_mob + + def get_tip(self): + # Very specific to the LaTeX representation + # of a brace, but it's the only way I can think + # of to get the tip regardless of orientation. + return self.get_all_points()[self.tip_point_index] + + def get_direction(self): + vect = self.get_tip() - self.get_center() + return vect/np.linalg.norm(vect) + +class BraceLabel(VMobject): + CONFIG = { + "label_constructor" : TexMobject, + "label_scale" : 1, + } + def __init__(self, obj, text, brace_direction = DOWN, **kwargs): + VMobject.__init__(self, **kwargs) + self.brace_direction = brace_direction + if isinstance(obj, list): obj = VMobject(*obj) + self.brace = Brace(obj, brace_direction, **kwargs) + + if isinstance(text, tuple) or isinstance(text, list): + self.label = self.label_constructor(*text, **kwargs) + else: self.label = self.label_constructor(str(text)) + if self.label_scale != 1: self.label.scale(self.label_scale) + + self.brace.put_at_tip(self.label) + self.submobjects = [self.brace, self.label] + + def creation_anim(self, label_anim = FadeIn, brace_anim = GrowFromCenter): + return AnimationGroup(brace_anim(self.brace), label_anim(self.label)) + + def shift_brace(self, obj, **kwargs): + if isinstance(obj, list): obj = VMobject(*obj) + self.brace = Brace(obj, self.brace_direction, **kwargs) + self.brace.put_at_tip(self.label) + self.submobjects[0] = self.brace + return self + + def change_label(self, *text, **kwargs): + self.label = self.label_constructor(*text, **kwargs) + if self.label_scale != 1: self.label.scale(self.label_scale) + + self.brace.put_at_tip(self.label) + self.submobjects[1] = self.label + return self + + def change_brace_label(self, obj, *text): + self.shift_brace(obj) + self.change_label(*text) + return self + + def copy(self): + copy_mobject = copy.copy(self) + copy_mobject.brace = self.brace.copy() + copy_mobject.label = self.label.copy() + copy_mobject.submobjects = [copy_mobject.brace, copy_mobject.label] + + return copy_mobject + +class BraceText(BraceLabel): + CONFIG = { + "label_constructor" : TextMobject + } diff --git a/topics/objects.py b/mobject/svg/drawings.py similarity index 79% rename from topics/objects.py rename to mobject/svg/drawings.py index ee0d4deb..02e370aa 100644 --- a/topics/objects.py +++ b/mobject/svg/drawings.py @@ -1,13 +1,15 @@ +from __future__ import absolute_import + from constants import * from mobject.mobject import Mobject -from mobject.svg_mobject import SVGMobject -from mobject.tex_mobject import Brace -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 mobject.vectorized_mobject import VectorizedPoint +from mobject.svg.svg_mobject import SVGMobject +from mobject.svg.brace import Brace +from mobject.svg.tex_mobject import TexMobject +from mobject.svg.tex_mobject import TextMobject +from mobject.types.vectorized_mobject import VGroup +from mobject.types.vectorized_mobject import VMobject +from mobject.types.vectorized_mobject import VectorizedPoint from animation.animation import Animation from animation.composition import AnimationGroup @@ -17,13 +19,13 @@ from animation.transform import ApplyMethod from animation.creation import FadeIn from animation.creation import GrowFromCenter -from topics.geometry import Arc -from topics.geometry import Circle -from topics.geometry import Line -from topics.geometry import Polygon -from topics.geometry import Rectangle -from topics.geometry import Square -from topics.geometry import SurroundingRectangle +from mobject.geometry import Arc +from mobject.geometry import Circle +from mobject.geometry import Line +from mobject.geometry import Polygon +from mobject.geometry import Rectangle +from mobject.geometry import Square +from mobject.shape_matchers import SurroundingRectangle from topics.three_dimensions import Cube from utils.config_ops import digest_config from utils.config_ops import digest_locals @@ -583,131 +585,5 @@ class Car(SVGMobject): def get_rear_light(self): return self[1][8] -class MoveCar(ApplyMethod): - CONFIG = { - "moving_forward" : True, - } - def __init__(self, car, target_point, **kwargs): - ApplyMethod.__init__(self, car.move_to, target_point, **kwargs) - displacement = self.target_mobject.get_right()-self.starting_mobject.get_right() - distance = np.linalg.norm(displacement) - if not self.moving_forward: - distance *= -1 - tire_radius = car.get_tires()[0].get_width()/2 - self.total_tire_radians = -distance/tire_radius - def update_mobject(self, alpha): - ApplyMethod.update_mobject(self, alpha) - if alpha == 0: - return - radians = alpha*self.total_tire_radians - for tire in self.mobject.get_tires(): - tire.rotate_in_place(radians) -#TODO: Where should this live? -class Broadcast(LaggedStart): - CONFIG = { - "small_radius" : 0.0, - "big_radius" : 5, - "n_circles" : 5, - "start_stroke_width" : 8, - "color" : WHITE, - "remover" : True, - "lag_ratio" : 0.7, - "run_time" : 3, - "remover" : True, - } - def __init__(self, focal_point, **kwargs): - digest_config(self, kwargs) - circles = VGroup() - for x in range(self.n_circles): - circle = Circle( - radius = self.big_radius, - stroke_color = BLACK, - stroke_width = 0, - ) - circle.move_to(focal_point) - circle.save_state() - circle.scale_to_fit_width(self.small_radius*2) - circle.set_stroke(self.color, self.start_stroke_width) - circles.add(circle) - LaggedStart.__init__( - self, ApplyMethod, circles, - lambda c : (c.restore,), - **kwargs - - ) - -class BraceLabel(VMobject): - CONFIG = { - "label_constructor" : TexMobject, - "label_scale" : 1, - } - def __init__(self, obj, text, brace_direction = DOWN, **kwargs): - VMobject.__init__(self, **kwargs) - self.brace_direction = brace_direction - if isinstance(obj, list): obj = VMobject(*obj) - self.brace = Brace(obj, brace_direction, **kwargs) - - if isinstance(text, tuple) or isinstance(text, list): - self.label = self.label_constructor(*text, **kwargs) - else: self.label = self.label_constructor(str(text)) - if self.label_scale != 1: self.label.scale(self.label_scale) - - self.brace.put_at_tip(self.label) - self.submobjects = [self.brace, self.label] - - def creation_anim(self, label_anim = FadeIn, brace_anim = GrowFromCenter): - return AnimationGroup(brace_anim(self.brace), label_anim(self.label)) - - def shift_brace(self, obj, **kwargs): - if isinstance(obj, list): obj = VMobject(*obj) - self.brace = Brace(obj, self.brace_direction, **kwargs) - self.brace.put_at_tip(self.label) - self.submobjects[0] = self.brace - return self - - def change_label(self, *text, **kwargs): - self.label = self.label_constructor(*text, **kwargs) - if self.label_scale != 1: self.label.scale(self.label_scale) - - self.brace.put_at_tip(self.label) - self.submobjects[1] = self.label - return self - - def change_brace_label(self, obj, *text): - self.shift_brace(obj) - self.change_label(*text) - return self - - def copy(self): - copy_mobject = copy.copy(self) - copy_mobject.brace = self.brace.copy() - copy_mobject.label = self.label.copy() - copy_mobject.submobjects = [copy_mobject.brace, copy_mobject.label] - - return copy_mobject - -class BraceText(BraceLabel): - CONFIG = { - "label_constructor" : TextMobject - } - -class DashedMobject(VMobject): - CONFIG = { - "dashes_num" : 15, - "spacing" : 0.5, - "color" : WHITE - } - def __init__(self, mob, **kwargs): - digest_locals(self) - VMobject.__init__(self, **kwargs) - - buff = float(self.spacing) / self.dashes_num - - for i in range(self.dashes_num): - a = ((1+buff) * i)/self.dashes_num - b = 1-((1+buff) * (self.dashes_num-1-i)) / self.dashes_num - dash = VMobject(color = self.color) - dash.pointwise_become_partial(mob, a, b) - self.submobjects.append(dash) diff --git a/mobject/svg_mobject.py b/mobject/svg/svg_mobject.py similarity index 98% rename from mobject/svg_mobject.py rename to mobject/svg/svg_mobject.py index 3644bd07..23907ce6 100644 --- a/mobject/svg_mobject.py +++ b/mobject/svg/svg_mobject.py @@ -6,13 +6,13 @@ import warnings from xml.dom import minidom from constants import * -from topics.geometry import Circle -from topics.geometry import Rectangle +from mobject.geometry import Circle +from mobject.geometry import Rectangle from utils.bezier import is_closed from utils.config_ops import digest_config from utils.config_ops import digest_locals -from vectorized_mobject import VGroup -from vectorized_mobject import VMobject +from mobject.types.vectorized_mobject import VGroup +from mobject.types.vectorized_mobject import VMobject def string_to_numbers(num_string): num_string = num_string.replace("-",",-") diff --git a/mobject/tex_mobject.py b/mobject/svg/tex_mobject.py similarity index 82% rename from mobject/tex_mobject.py rename to mobject/svg/tex_mobject.py index b6c19f7c..bdc48d43 100644 --- a/mobject/tex_mobject.py +++ b/mobject/svg/tex_mobject.py @@ -2,11 +2,11 @@ from constants import * from svg_mobject import SVGMobject from svg_mobject import VMobjectFromSVGPathstring -from topics.geometry import BackgroundRectangle +from mobject.shape_matchers import BackgroundRectangle from utils.config_ops import digest_config -from vectorized_mobject import VGroup -from vectorized_mobject import VMobject -from vectorized_mobject import VectorizedPoint +from mobject.types.vectorized_mobject import VGroup +from mobject.types.vectorized_mobject import VMobject +from mobject.types.vectorized_mobject import VectorizedPoint import collections import operator as op @@ -231,68 +231,6 @@ class TextMobject(TexMobject): "alignment" : "\\centering", } -class Brace(TexMobject): - CONFIG = { - "buff" : 0.2, - "width_multiplier" : 2, - "max_num_quads" : 15, - "min_num_quads" : 0, - } - def __init__(self, mobject, direction = DOWN, **kwargs): - digest_config(self, kwargs, locals()) - angle = -np.arctan2(*direction[:2]) + np.pi - mobject.rotate(-angle, about_point = ORIGIN) - left = mobject.get_corner(DOWN+LEFT) - right = mobject.get_corner(DOWN+RIGHT) - target_width = right[0]-left[0] - - ## Adding int(target_width) qquads gives approximately the right width - num_quads = np.clip( - int(self.width_multiplier*target_width), - self.min_num_quads, self.max_num_quads - ) - tex_string = "\\underbrace{%s}"%(num_quads*"\\qquad") - TexMobject.__init__(self, tex_string, **kwargs) - self.tip_point_index = np.argmin(self.get_all_points()[:,1]) - self.stretch_to_fit_width(target_width) - self.shift(left - self.get_corner(UP+LEFT) + self.buff*DOWN) - for mob in mobject, self: - mob.rotate(angle, about_point = ORIGIN) - - def put_at_tip(self, mob, use_next_to = True, **kwargs): - if use_next_to: - mob.next_to( - self.get_tip(), - np.round(self.get_direction()), - **kwargs - ) - else: - mob.move_to(self.get_tip()) - buff = kwargs.get("buff", DEFAULT_MOBJECT_TO_MOBJECT_BUFFER) - shift_distance = mob.get_width()/2.0+buff - mob.shift(self.get_direction()*shift_distance) - return self - - def get_text(self, *text, **kwargs): - text_mob = TextMobject(*text) - self.put_at_tip(text_mob, **kwargs) - return text_mob - - def get_tex(self, *tex, **kwargs): - tex_mob = TexMobject(*tex) - self.put_at_tip(tex_mob, **kwargs) - return tex_mob - - def get_tip(self): - # Very specific to the LaTeX representation - # of a brace, but it's the only way I can think - # of to get the tip regardless of orientation. - return self.get_all_points()[self.tip_point_index] - - def get_direction(self): - vect = self.get_tip() - self.get_center() - return vect/np.linalg.norm(vect) - class BulletedList(TextMobject): CONFIG = { "buff" : MED_LARGE_BUFF, diff --git a/mobject/types/__init__.py b/mobject/types/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/mobject/image_mobject.py b/mobject/types/image_mobject.py similarity index 98% rename from mobject/image_mobject.py rename to mobject/types/image_mobject.py index 9dac5227..17ca1121 100644 --- a/mobject/image_mobject.py +++ b/mobject/types/image_mobject.py @@ -10,7 +10,6 @@ from random import random from constants import * from mobject.mobject import Mobject -from mobject.point_cloud_mobject import PMobject from utils.bezier import interpolate from utils.color import color_to_int_rgb from utils.color import interpolate_color diff --git a/mobject/point_cloud_mobject.py b/mobject/types/point_cloud_mobject.py similarity index 100% rename from mobject/point_cloud_mobject.py rename to mobject/types/point_cloud_mobject.py diff --git a/mobject/vectorized_mobject.py b/mobject/types/vectorized_mobject.py similarity index 96% rename from mobject/vectorized_mobject.py rename to mobject/types/vectorized_mobject.py index e4635f7a..280f732a 100644 --- a/mobject/vectorized_mobject.py +++ b/mobject/types/vectorized_mobject.py @@ -493,3 +493,21 @@ class VectorizedPoint(VMobject): def set_location(self,new_loc): self.set_points(np.array([new_loc])) +class DashedMobject(VMobject): + CONFIG = { + "dashes_num" : 15, + "spacing" : 0.5, + "color" : WHITE + } + def __init__(self, mobject, **kwargs): + VMobject.__init__(self, **kwargs) + + buff = float(self.spacing) / self.dashes_num + + for i in range(self.dashes_num): + a = ((1+buff) * i)/self.dashes_num + b = 1-((1+buff) * (self.dashes_num-1-i)) / self.dashes_num + dash = VMobject(color = self.color) + dash.pointwise_become_partial(mobject, a, b) + self.submobjects.append(dash) + diff --git a/mobject/value_tracker.py b/mobject/value_tracker.py new file mode 100644 index 00000000..48f8ba59 --- /dev/null +++ b/mobject/value_tracker.py @@ -0,0 +1,43 @@ +from __future__ import absolute_import + +import numpy as np + +from constants import * + +from mobject.types.vectorized_mobject import VectorizedPoint + +# TODO: Rather than using VectorizedPoint, there should be some UndisplayedPointSet type + +class ValueTracker(VectorizedPoint): + """ + Note meant to be displayed. Instead the position encodes some + number, often one which another animation or continual_animation + uses for its update function, and by treating it as a mobject it can + still be animated and manipulated just like anything else. + """ + def __init__(self, value = 0, **kwargs): + VectorizedPoint.__init__(self, **kwargs) + self.set_value(value) + + def get_value(self): + return self.get_center()[0] + + def set_value(self, value): + self.move_to(value*RIGHT) + return self + + def increment_value(self, d_value): + self.set_value(self.get_value() + d_value) + +class ExponentialValueTracker(ValueTracker): + """ + Operates just like ValueTracker, except it encodes the value as the + exponential of a position coordinate, which changes how interpolation + behaves + """ + def get_value(self): + return np.exp(self.get_center()[0]) + + def set_value(self, value): + self.move_to(np.log(value)*RIGHT) + return self diff --git a/old_projects/uncertainty.py b/old_projects/uncertainty.py index cf353c32..7e4bd593 100644 --- a/old_projects/uncertainty.py +++ b/old_projects/uncertainty.py @@ -7,40 +7,6 @@ from old_projects.fourier import * FREQUENCY_COLOR = RED USE_ALMOST_FOURIER_BY_DEFAULT = False -class ValueTracker(VectorizedPoint): - """ - Note meant to be displayed. Instead the position encodes some - number, often one which another animation or continual_animation - uses for its update function, and by treating it as a mobject it can - still be animated and manipulated just like anything else. - """ - def __init__(self, value = 0, **kwargs): - VectorizedPoint.__init__(self, **kwargs) - self.set_value(value) - - def get_value(self): - return self.get_center()[0] - - def set_value(self, value): - self.move_to(value*RIGHT) - return self - - def increment_value(self, d_value): - self.set_value(self.get_value() + d_value) - -class ExponentialValueTracker(ValueTracker): - """ - Operates just like ValueTracker, except it encodes the value as the - exponential of a position coordinate, which changes how interpolation - behaves - """ - def get_value(self): - return np.exp(self.get_center()[0]) - - def set_value(self, value): - self.move_to(np.log(value)*RIGHT) - return self - class GaussianDistributionWrapper(Line): """ This is meant to encode a 2d normal distribution as diff --git a/once_useful_constructs/arithmetic.py b/once_useful_constructs/arithmetic.py index 542e43c8..173ecc73 100644 --- a/once_useful_constructs/arithmetic.py +++ b/once_useful_constructs/arithmetic.py @@ -3,7 +3,7 @@ import numpy as np from animation.animation import Animation from constants import * -from mobject.tex_mobject import TexMobject +from mobject.svg.tex_mobject import TexMobject from scene.scene import Scene class RearrangeEquation(Scene): diff --git a/once_useful_constructs/combinatorics.py b/once_useful_constructs/combinatorics.py index 1ef0ceb1..42340dfd 100644 --- a/once_useful_constructs/combinatorics.py +++ b/once_useful_constructs/combinatorics.py @@ -1,7 +1,7 @@ from constants import * -from mobject.tex_mobject import TexMobject -from mobject.vectorized_mobject import VMobject +from mobject.svg.tex_mobject import TexMobject +from mobject.types.vectorized_mobject import VMobject from scene.scene import Scene from utils.simple_functions import choose diff --git a/once_useful_constructs/counting.py b/once_useful_constructs/counting.py index 8b43ae5f..8b8edeb0 100644 --- a/once_useful_constructs/counting.py +++ b/once_useful_constructs/counting.py @@ -1,18 +1,18 @@ from constants import * from mobject.mobject import Mobject -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 mobject.svg.tex_mobject import TexMobject +from mobject.svg.tex_mobject import TextMobject +from mobject.types.vectorized_mobject import VGroup +from mobject.types.vectorized_mobject import VMobject from animation.creation import ShowCreation from animation.creation import FadeIn from animation.transform import MoveToTarget from animation.transform import Transform -from topics.geometry import Arrow -from topics.geometry import Circle -from topics.geometry import Dot +from mobject.geometry import Arrow +from mobject.geometry import Circle +from mobject.geometry import Dot from scene.scene import Scene diff --git a/once_useful_constructs/fractals.py b/once_useful_constructs/fractals.py index 5f2e7200..f6d24eeb 100644 --- a/once_useful_constructs/fractals.py +++ b/once_useful_constructs/fractals.py @@ -3,15 +3,15 @@ from animation.transform import Transform from pi_creature.pi_creature import PiCreature from pi_creature.pi_creature import Randolph from pi_creature.pi_creature import get_all_pi_creature_modes -from mobject.vectorized_mobject import VGroup -from mobject.vectorized_mobject import VMobject -from mobject.vectorized_mobject import VectorizedPoint +from mobject.types.vectorized_mobject import VGroup +from mobject.types.vectorized_mobject import VMobject +from mobject.types.vectorized_mobject import VectorizedPoint from scene.scene import Scene -from topics.geometry import Circle -from topics.geometry import Line -from topics.geometry import Polygon -from topics.geometry import RegularPolygon -from topics.geometry import Square +from mobject.geometry import Circle +from mobject.geometry import Line +from mobject.geometry import Polygon +from mobject.geometry import RegularPolygon +from mobject.geometry import Square from utils.bezier import interpolate from utils.color import color_gradient from utils.config_ops import digest_config diff --git a/once_useful_constructs/light.py b/once_useful_constructs/light.py index 71b4895c..76165cd5 100644 --- a/once_useful_constructs/light.py +++ b/once_useful_constructs/light.py @@ -1,4 +1,34 @@ -from big_ol_pile_of_manim_imports import * +from constants import * + +from mobject.geometry import AnnularSector +from mobject.geometry import Arc +from mobject.mobject import Mobject +from mobject.svg.svg_mobject import SVGMobject +from mobject.svg.tex_mobject import TexMobject +from mobject.types.vectorized_mobject import VGroup +from mobject.types.vectorized_mobject import VMobject +from mobject.types.vectorized_mobject import VectorizedPoint + +from continual_animation.continual_animation import ContinualAnimation + +from animation.animation import Animation +from animation.composition import LaggedStart +from animation.transform import ApplyMethod +from animation.transform import Transform +from animation.creation import FadeIn +from animation.creation import FadeOut + +from camera.camera import Camera +from scene.scene import Scene +from topics.three_dimensions import ThreeDCamera +from topics.three_dimensions import ThreeDScene + +from utils.space_ops import angle_between +from utils.space_ops import angle_between_vectors +from utils.space_ops import project_along_vector +from utils.space_ops import rotate_vector +from utils.space_ops import rotation_matrix +from utils.space_ops import z_to_vector from scipy.spatial import ConvexHull from traceback import * diff --git a/mobject/region.py b/once_useful_constructs/region.py similarity index 100% rename from mobject/region.py rename to once_useful_constructs/region.py diff --git a/pi_creature/pi_creature.py b/pi_creature/pi_creature.py index 479c542d..ea688c46 100644 --- a/pi_creature/pi_creature.py +++ b/pi_creature/pi_creature.py @@ -3,12 +3,12 @@ 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 mobject.svg.svg_mobject import SVGMobject +from mobject.svg.tex_mobject import TextMobject +from mobject.types.vectorized_mobject import VGroup +from mobject.types.vectorized_mobject import VMobject -from topics.objects import ThoughtBubble +from mobject.svg.drawings import ThoughtBubble from animation.transform import Transform from utils.config_ops import digest_config diff --git a/pi_creature/pi_creature_animations.py b/pi_creature/pi_creature_animations.py index 0a21ec39..8ee4753b 100644 --- a/pi_creature/pi_creature_animations.py +++ b/pi_creature/pi_creature_animations.py @@ -4,7 +4,7 @@ from constants import * from mobject.mobject import Group -from topics.objects import SpeechBubble +from mobject.svg.drawings import SpeechBubble from animation.creation import ShowCreation from animation.creation import Write diff --git a/pi_creature/pi_creature_scene.py b/pi_creature/pi_creature_scene.py index b8f46a42..4bcf9e72 100644 --- a/pi_creature/pi_creature_scene.py +++ b/pi_creature/pi_creature_scene.py @@ -6,11 +6,11 @@ import random from constants import * -from mobject.vectorized_mobject import VGroup +from mobject.types.vectorized_mobject import VGroup -from topics.geometry import ScreenRectangle -from topics.objects import SpeechBubble -from topics.objects import ThoughtBubble +from mobject.frame import ScreenRectangle +from mobject.svg.drawings import SpeechBubble +from mobject.svg.drawings import ThoughtBubble from animation.transform import ApplyMethod from animation.transform import ReplacementTransform diff --git a/scene/moving_camera_scene.py b/scene/moving_camera_scene.py index 61615984..70f0f158 100644 --- a/scene/moving_camera_scene.py +++ b/scene/moving_camera_scene.py @@ -4,7 +4,7 @@ from constants import * from scene.scene import Scene from camera.moving_camera import MovingCamera -from topics.geometry import ScreenRectangle +from mobject.frame import ScreenRectangle class MovingCameraScene(Scene): def setup(self): diff --git a/scene/scene.py b/scene/scene.py index fb94161a..1817a326 100644 --- a/scene/scene.py +++ b/scene/scene.py @@ -20,7 +20,7 @@ from animation.transform import MoveToTarget from camera.camera import Camera from continual_animation.continual_animation import ContinualAnimation from mobject.mobject import Mobject -from mobject.vectorized_mobject import VMobject +from mobject.types.vectorized_mobject import VMobject from utils.iterables import list_update from container.container import Container diff --git a/scene/zoomed_scene.py b/scene/zoomed_scene.py index e85e27c4..a5df8796 100644 --- a/scene/zoomed_scene.py +++ b/scene/zoomed_scene.py @@ -7,7 +7,7 @@ from animation.creation import FadeIn from camera.camera import Camera from camera.moving_camera import MovingCamera from mobject.mobject import Mobject -from topics.geometry import Rectangle +from mobject.geometry import Rectangle from constants import * diff --git a/topics/common_scenes.py b/topics/common_scenes.py index bc3b88b3..2fccf359 100644 --- a/topics/common_scenes.py +++ b/topics/common_scenes.py @@ -8,17 +8,17 @@ 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 -from mobject.vectorized_mobject import VGroup +from mobject.svg.tex_mobject import TexMobject +from mobject.svg.tex_mobject import TextMobject +from mobject.types.vectorized_mobject import VGroup from scene.scene import Scene from pi_creature.pi_creature_animations import Blink from pi_creature.pi_creature import Mortimer from pi_creature.pi_creature import Randolph -from topics.geometry import DashedLine -from topics.geometry import Rectangle -from topics.geometry import Square -from topics.objects import PatreonLogo +from mobject.geometry import DashedLine +from mobject.geometry import Rectangle +from mobject.geometry import Square +from mobject.svg.drawings import PatreonLogo class OpeningQuote(Scene): CONFIG = { diff --git a/topics/complex_numbers.py b/topics/complex_numbers.py index df566378..7d677b64 100644 --- a/topics/complex_numbers.py +++ b/topics/complex_numbers.py @@ -7,9 +7,9 @@ from animation.creation import ShowCreation from animation.movement import SmoothedVectorizedHomotopy from animation.transform import ApplyPointwiseFunction from animation.transform import MoveToTarget -from mobject.tex_mobject import TexMobject -from mobject.tex_mobject import TextMobject -from mobject.vectorized_mobject import VGroup +from mobject.svg.tex_mobject import TexMobject +from mobject.svg.tex_mobject import TextMobject +from mobject.types.vectorized_mobject import VGroup from number_line import NumberPlane from scene.scene import Scene from utils.config_ops import digest_config diff --git a/topics/functions.py b/topics/functions.py index e72a10ed..ddf94bf5 100644 --- a/topics/functions.py +++ b/topics/functions.py @@ -1,6 +1,6 @@ from scipy import integrate -from mobject.vectorized_mobject import VMobject +from mobject.types.vectorized_mobject import VMobject from utils.config_ops import digest_config from constants import * diff --git a/topics/graph_scene.py b/topics/graph_scene.py index 84e2918f..7dbe5eca 100644 --- a/topics/graph_scene.py +++ b/topics/graph_scene.py @@ -1,19 +1,19 @@ from constants import * from scene.scene import Scene -# from topics.geometry import +# from mobject.geometry import from animation.creation import ShowCreation from animation.update import UpdateFromAlphaFunc from animation.creation import Write from animation.transform import Transform -from mobject.tex_mobject import TexMobject -from mobject.tex_mobject import TextMobject -from mobject.vectorized_mobject import VGroup -from mobject.vectorized_mobject import VectorizedPoint +from mobject.svg.tex_mobject import TexMobject +from mobject.svg.tex_mobject import TextMobject +from mobject.types.vectorized_mobject import VGroup +from mobject.types.vectorized_mobject import VectorizedPoint from topics.functions import ParametricFunction -from topics.geometry import DashedLine -from topics.geometry import Line -from topics.geometry import Rectangle +from mobject.geometry import DashedLine +from mobject.geometry import Line +from mobject.geometry import Rectangle from topics.number_line import NumberLine from utils.bezier import interpolate from utils.color import color_gradient diff --git a/topics/matrix.py b/topics/matrix.py index 74630598..985651a5 100644 --- a/topics/matrix.py +++ b/topics/matrix.py @@ -8,17 +8,17 @@ from animation.transform import ApplyPointwiseFunction from animation.creation import FadeOut from animation.transform import Transform from mobject.mobject import Mobject -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 mobject.svg.tex_mobject import TexMobject +from mobject.svg.tex_mobject import TextMobject +from mobject.types.vectorized_mobject import VGroup +from mobject.types.vectorized_mobject import VMobject from scene.scene import Scene -from topics.geometry import Arrow -from topics.geometry import BackgroundRectangle -from topics.geometry import Circle -from topics.geometry import Dot -from topics.geometry import Line -from topics.geometry import Vector +from mobject.geometry import Arrow +from mobject.shape_matchers import BackgroundRectangle +from mobject.geometry import Circle +from mobject.geometry import Dot +from mobject.geometry import Line +from mobject.geometry import Vector from topics.number_line import Axes from topics.number_line import NumberPlane diff --git a/topics/number_line.py b/topics/number_line.py index 7fcfefe4..c1e8f6cb 100644 --- a/topics/number_line.py +++ b/topics/number_line.py @@ -1,12 +1,12 @@ from constants import * -from mobject.tex_mobject import TexMobject -from mobject.vectorized_mobject import VGroup -from mobject.vectorized_mobject import VMobject +from mobject.svg.tex_mobject import TexMobject +from mobject.types.vectorized_mobject import VGroup +from mobject.types.vectorized_mobject import VMobject from scene.scene import Scene from topics.functions import ParametricFunction -from topics.geometry import Arrow -from topics.geometry import Line +from mobject.geometry import Arrow +from mobject.geometry import Line from utils.bezier import interpolate from utils.config_ops import digest_config from utils.space_ops import angle_of_vector diff --git a/topics/numerals.py b/topics/numerals.py index 6ee454a7..0f8bc901 100644 --- a/topics/numerals.py +++ b/topics/numerals.py @@ -2,12 +2,12 @@ from animation.animation import Animation from constants import * from continual_animation.continual_animation import ContinualAnimation -from mobject.tex_mobject import TexMobject -from mobject.vectorized_mobject import VGroup -from mobject.vectorized_mobject import VMobject -from mobject.vectorized_mobject import VectorizedPoint +from mobject.svg.tex_mobject import TexMobject +from mobject.types.vectorized_mobject import VGroup +from mobject.types.vectorized_mobject import VMobject +from mobject.types.vectorized_mobject import VectorizedPoint from scene.scene import Scene -from topics.geometry import BackgroundRectangle +from mobject.shape_matchers import BackgroundRectangle from utils.bezier import interpolate from utils.config_ops import digest_config diff --git a/topics/probability.py b/topics/probability.py index 3034859a..5752cbc4 100644 --- a/topics/probability.py +++ b/topics/probability.py @@ -8,19 +8,19 @@ from animation.transform import Transform from animation.update import UpdateFromFunc from mobject.mobject import Mobject -from mobject.svg_mobject import SVGMobject -from mobject.tex_mobject import Brace -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 mobject.vectorized_mobject import VectorizedPoint -from topics.geometry import Arc -from topics.geometry import Circle -from topics.geometry import Line -from topics.geometry import Polygon -from topics.geometry import Rectangle -from topics.geometry import Square +from mobject.svg.svg_mobject import SVGMobject +from mobject.svg.brace import Brace +from mobject.svg.tex_mobject import TexMobject +from mobject.svg.tex_mobject import TextMobject +from mobject.types.vectorized_mobject import VGroup +from mobject.types.vectorized_mobject import VMobject +from mobject.types.vectorized_mobject import VectorizedPoint +from mobject.geometry import Arc +from mobject.geometry import Circle +from mobject.geometry import Line +from mobject.geometry import Polygon +from mobject.geometry import Rectangle +from mobject.geometry import Square from utils.bezier import interpolate from utils.color import average_color diff --git a/topics/three_dimensions.py b/topics/three_dimensions.py index 54bd0cb9..0718f4ed 100644 --- a/topics/three_dimensions.py +++ b/topics/three_dimensions.py @@ -4,12 +4,12 @@ from constants import * from continual_animation.continual_animation import ContinualMovement from animation.transform import ApplyMethod from camera.camera import Camera -from mobject.vectorized_mobject import VGroup -from mobject.vectorized_mobject import VMobject -from mobject.vectorized_mobject import VectorizedPoint +from mobject.types.vectorized_mobject import VGroup +from mobject.types.vectorized_mobject import VMobject +from mobject.types.vectorized_mobject import VectorizedPoint from scene.scene import Scene -from topics.geometry import Line -from topics.geometry import Square +from mobject.geometry import Line +from mobject.geometry import Square from utils.bezier import interpolate from utils.iterables import list_update diff --git a/topics/vector_space_scene.py b/topics/vector_space_scene.py index d7bdac44..591eb966 100644 --- a/topics/vector_space_scene.py +++ b/topics/vector_space_scene.py @@ -9,18 +9,18 @@ from animation.transform import ApplyPointwiseFunction from animation.creation import FadeOut from animation.transform import Transform from mobject.mobject import Mobject -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 mobject.svg.tex_mobject import TexMobject +from mobject.svg.tex_mobject import TextMobject +from mobject.types.vectorized_mobject import VGroup +from mobject.types.vectorized_mobject import VMobject from scene.scene import Scene -from topics.geometry import Arrow -from topics.geometry import BackgroundRectangle -from topics.geometry import Circle -from topics.geometry import Dot -from topics.geometry import Line -from topics.geometry import Square -from topics.geometry import Vector +from mobject.geometry import Arrow +from mobject.shape_matchers import BackgroundRectangle +from mobject.geometry import Circle +from mobject.geometry import Dot +from mobject.geometry import Line +from mobject.geometry import Square +from mobject.geometry import Vector from topics.number_line import Axes from topics.number_line import NumberPlane diff --git a/utils/space_ops.py b/utils/space_ops.py index 155273a6..c9f1506a 100644 --- a/utils/space_ops.py +++ b/utils/space_ops.py @@ -101,12 +101,3 @@ def center_of_mass(points): points = [np.array(point).astype("float") for point in points] return sum(points) / len(points) - -# TODO: It feels like this should live elsewhere - - - - - - - From 16dde2f84d803da8ae3361650baaed8c7fea3e54 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Sat, 31 Mar 2018 18:13:16 -0700 Subject: [PATCH 08/17] Created for_3b1b_videos folder. This is meant to separate things specific to 3b1b, like pi_creatures and certain other specific scenes, from the remainder of the library --- big_ol_pile_of_manim_imports.py | 8 ++++---- {pi_creature => for_3b1b_videos}/__init__.py | 0 {topics => for_3b1b_videos}/common_scenes.py | 9 ++++++--- {pi_creature => for_3b1b_videos}/pi_creature.py | 0 .../pi_creature_animations.py | 0 .../pi_creature_scene.py | 12 ++++++------ mobject/svg/drawings.py | 2 +- once_useful_constructs/fractals.py | 6 +++--- topics/probability.py | 2 +- 9 files changed, 21 insertions(+), 18 deletions(-) rename {pi_creature => for_3b1b_videos}/__init__.py (100%) rename {topics => for_3b1b_videos}/common_scenes.py (97%) rename {pi_creature => for_3b1b_videos}/pi_creature.py (100%) rename {pi_creature => for_3b1b_videos}/pi_creature_animations.py (100%) rename {pi_creature => for_3b1b_videos}/pi_creature_scene.py (97%) diff --git a/big_ol_pile_of_manim_imports.py b/big_ol_pile_of_manim_imports.py index 0dbd9e73..bb1ef166 100644 --- a/big_ol_pile_of_manim_imports.py +++ b/big_ol_pile_of_manim_imports.py @@ -45,9 +45,10 @@ from mobject.types.image_mobject import * from mobject.types.point_cloud_mobject import * from mobject.types.vectorized_mobject import * -from pi_creature.pi_creature import * -from pi_creature.pi_creature_animations import * -from pi_creature.pi_creature_scene import * +from for_3b1b_videos.pi_creature import * +from for_3b1b_videos.pi_creature_animations import * +from for_3b1b_videos.pi_creature_scene import * +from for_3b1b_videos.common_scenes import * from scene.moving_camera_scene import * from scene.reconfigurable_scene import * @@ -62,7 +63,6 @@ from once_useful_constructs.fractals import * from once_useful_constructs.graph_theory import * from once_useful_constructs.light import * -from topics.common_scenes import * from topics.complex_numbers import * from topics.functions import * from topics.graph_scene import * diff --git a/pi_creature/__init__.py b/for_3b1b_videos/__init__.py similarity index 100% rename from pi_creature/__init__.py rename to for_3b1b_videos/__init__.py diff --git a/topics/common_scenes.py b/for_3b1b_videos/common_scenes.py similarity index 97% rename from topics/common_scenes.py rename to for_3b1b_videos/common_scenes.py index 2fccf359..f6646855 100644 --- a/topics/common_scenes.py +++ b/for_3b1b_videos/common_scenes.py @@ -1,3 +1,6 @@ +from __future__ import absolute_import + +import random from constants import * @@ -12,9 +15,9 @@ from mobject.svg.tex_mobject import TexMobject from mobject.svg.tex_mobject import TextMobject from mobject.types.vectorized_mobject import VGroup from scene.scene import Scene -from pi_creature.pi_creature_animations import Blink -from pi_creature.pi_creature import Mortimer -from pi_creature.pi_creature import Randolph +from for_3b1b_videos.pi_creature_animations import Blink +from for_3b1b_videos.pi_creature import Mortimer +from for_3b1b_videos.pi_creature import Randolph from mobject.geometry import DashedLine from mobject.geometry import Rectangle from mobject.geometry import Square diff --git a/pi_creature/pi_creature.py b/for_3b1b_videos/pi_creature.py similarity index 100% rename from pi_creature/pi_creature.py rename to for_3b1b_videos/pi_creature.py diff --git a/pi_creature/pi_creature_animations.py b/for_3b1b_videos/pi_creature_animations.py similarity index 100% rename from pi_creature/pi_creature_animations.py rename to for_3b1b_videos/pi_creature_animations.py diff --git a/pi_creature/pi_creature_scene.py b/for_3b1b_videos/pi_creature_scene.py similarity index 97% rename from pi_creature/pi_creature_scene.py rename to for_3b1b_videos/pi_creature_scene.py index 4bcf9e72..ba84a55d 100644 --- a/pi_creature/pi_creature_scene.py +++ b/for_3b1b_videos/pi_creature_scene.py @@ -15,12 +15,12 @@ from mobject.svg.drawings 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 for_3b1b_videos.pi_creature import PiCreature +from for_3b1b_videos.pi_creature import Mortimer +from for_3b1b_videos.pi_creature import Randolph +from for_3b1b_videos.pi_creature_animations import Blink +from for_3b1b_videos.pi_creature_animations import PiCreatureBubbleIntroduction +from for_3b1b_videos.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 diff --git a/mobject/svg/drawings.py b/mobject/svg/drawings.py index 02e370aa..9a76b2d6 100644 --- a/mobject/svg/drawings.py +++ b/mobject/svg/drawings.py @@ -520,7 +520,7 @@ class Car(SVGMobject): self.set_stroke(color = WHITE, width = 0) self.set_fill(self.color, opacity = 1) - from pi_creature.pi_creature import Randolph + from for_3b1b_videos.pi_creature import Randolph randy = Randolph(mode = "happy") randy.scale_to_fit_height(0.6*self.get_height()) randy.stretch(0.8, 0) diff --git a/once_useful_constructs/fractals.py b/once_useful_constructs/fractals.py index f6d24eeb..68283db8 100644 --- a/once_useful_constructs/fractals.py +++ b/once_useful_constructs/fractals.py @@ -1,8 +1,8 @@ from animation.creation import ShowCreation from animation.transform import Transform -from pi_creature.pi_creature import PiCreature -from pi_creature.pi_creature import Randolph -from pi_creature.pi_creature import get_all_pi_creature_modes +from for_3b1b_videos.pi_creature import PiCreature +from for_3b1b_videos.pi_creature import Randolph +from for_3b1b_videos.pi_creature import get_all_pi_creature_modes from mobject.types.vectorized_mobject import VGroup from mobject.types.vectorized_mobject import VMobject from mobject.types.vectorized_mobject import VectorizedPoint diff --git a/topics/probability.py b/topics/probability.py index 5752cbc4..90d3c49c 100644 --- a/topics/probability.py +++ b/topics/probability.py @@ -541,7 +541,7 @@ class PlayingCard(VGroup): return design def get_face_card_design(self, value, symbol): - from pi_creature.pi_creature import PiCreature + from for_3b1b_videos.pi_creature import PiCreature sub_rect = Rectangle( stroke_color = BLACK, fill_opacity = 0, From a61f5b044fbd82b8405892caa9748aa86c59fe53 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Sat, 31 Mar 2018 18:31:56 -0700 Subject: [PATCH 09/17] Added coordinate_systems.py in mobject/ to house Axes, NumberPlane, ComplexPlane --- mobject/coordinate_systems.py | 351 ++++++++++++++++++++++++++++++++++ old_projects/fourier.py | 1 - topics/complex_numbers.py | 80 +------- topics/matrix.py | 4 +- topics/number_line.py | 276 -------------------------- topics/vector_space_scene.py | 4 +- 6 files changed, 358 insertions(+), 358 deletions(-) create mode 100644 mobject/coordinate_systems.py diff --git a/mobject/coordinate_systems.py b/mobject/coordinate_systems.py new file mode 100644 index 00000000..0b92a04c --- /dev/null +++ b/mobject/coordinate_systems.py @@ -0,0 +1,351 @@ +from __future__ import absolute_import + +import numpy as np + +from constants import * + +from mobject.geometry import Arrow +from mobject.geometry import Line +from mobject.svg.tex_mobject import TexMobject +from mobject.types.vectorized_mobject import VGroup +from mobject.types.vectorized_mobject import VMobject +from topics.functions import ParametricFunction +from topics.number_line import NumberLine +from utils.config_ops import digest_config +from utils.space_ops import angle_of_vector +from utils.space_ops import R3_to_complex +from utils.space_ops import complex_to_R3 + +#TODO: There should be much more code reuse between Axes, NumberPlane and GraphScene + +class Axes(VGroup): + CONFIG = { + "propagate_style_to_family" : True, + "three_d" : False, + "number_line_config" : { + "color" : LIGHT_GREY, + "include_tip" : True, + }, + "x_axis_config" : {}, + "y_axis_config" : {}, + "z_axis_config" : {}, + "x_min" : -FRAME_X_RADIUS, + "x_max" : FRAME_X_RADIUS, + "y_min" : -FRAME_Y_RADIUS, + "y_max" : FRAME_Y_RADIUS, + "z_min" : -3.5, + "z_max" : 3.5, + "z_normal" : DOWN, + "default_num_graph_points" : 100, + } + def __init__(self, **kwargs): + VGroup.__init__(self, **kwargs) + self.x_axis = self.get_axis(self.x_min, self.x_max, self.x_axis_config) + self.y_axis = self.get_axis(self.y_min, self.y_max, self.y_axis_config) + self.y_axis.rotate(np.pi/2, about_point = ORIGIN) + self.add(self.x_axis, self.y_axis) + if self.three_d: + self.z_axis = self.get_axis(self.z_min, self.z_max, self.z_axis_config) + self.z_axis.rotate(-np.pi/2, UP, about_point = ORIGIN) + self.z_axis.rotate( + angle_of_vector(self.z_normal), OUT, + about_point = ORIGIN + ) + self.add(self.z_axis) + + def get_axis(self, min_val, max_val, extra_config): + config = dict(self.number_line_config) + config.update(extra_config) + return NumberLine(x_min = min_val, x_max = max_val, **config) + + def coords_to_point(self, x, y): + origin = self.x_axis.number_to_point(0) + x_axis_projection = self.x_axis.number_to_point(x) + y_axis_projection = self.y_axis.number_to_point(y) + return x_axis_projection + y_axis_projection - origin + + def point_to_coords(self, point): + return ( + self.x_axis.point_to_number(point), + self.y_axis.point_to_number(point), + ) + + def get_graph( + self, function, num_graph_points = None, + x_min = None, + x_max = None, + **kwargs + ): + kwargs["fill_opacity"] = kwargs.get("fill_opacity", 0) + kwargs["num_anchor_points"] = \ + num_graph_points or self.default_num_graph_points + x_min = x_min or self.x_min + x_max = x_max or self.x_max + graph = ParametricFunction( + lambda t : self.coords_to_point(t, function(t)), + t_min = x_min, + t_max = x_max, + **kwargs + ) + graph.underlying_function = function + return graph + + def input_to_graph_point(self, x, graph): + if hasattr(graph, "underlying_function"): + return self.coords_to_point(x, graph.underlying_function(x)) + else: + #binary search + lh, rh = 0, 1 + while abs(lh - rh) > 0.001: + mh = np.mean([lh, rh]) + hands = [lh, mh, rh] + points = map(graph.point_from_proportion, hands) + lx, mx, rx = map(self.x_axis.point_to_number, points) + if lx <= x and rx >= x: + if mx > x: + rh = mh + else: + lh = mh + elif lx <= x and rx <= x: + return points[2] + elif lx >= x and rx >= x: + return points[0] + elif lx > x and rx < x: + lh, rh = rh, lh + return points[1] + return self.coords_to_point(x, graph.underlying_function(x)) + +class ThreeDAxes(Axes): + CONFIG = { + "x_min" : -5.5, + "x_max" : 5.5, + "y_min" : -4.5, + "y_max" : 4.5, + "three_d" : True, + } + +class NumberPlane(VMobject): + CONFIG = { + "color" : BLUE_D, + "secondary_color" : BLUE_E, + "axes_color" : WHITE, + "secondary_stroke_width" : 1, + # TODO: Allow coordinate center of NumberPlane to not be at (0, 0) + "x_radius": None, + "y_radius": None, + "x_unit_size" : 1, + "y_unit_size" : 1, + "center_point" : ORIGIN, + "x_line_frequency" : 1, + "y_line_frequency" : 1, + "secondary_line_ratio" : 1, + "written_coordinate_height" : 0.2, + "propagate_style_to_family" : False, + "make_smooth_after_applying_functions" : True, + } + def generate_points(self): + if self.x_radius is None: + center_to_edge = (FRAME_X_RADIUS + abs(self.center_point[0])) + self.x_radius = center_to_edge / self.x_unit_size + if self.y_radius is None: + center_to_edge = (FRAME_Y_RADIUS + abs(self.center_point[1])) + self.y_radius = center_to_edge / self.y_unit_size + self.axes = VMobject() + self.main_lines = VMobject() + self.secondary_lines = VMobject() + tuples = [ + ( + self.x_radius, + self.x_line_frequency, + self.y_radius*DOWN, + self.y_radius*UP, + RIGHT + ), + ( + self.y_radius, + self.y_line_frequency, + self.x_radius*LEFT, + self.x_radius*RIGHT, + UP, + ), + ] + for radius, freq, start, end, unit in tuples: + main_range = np.arange(0, radius, freq) + step = freq/float(freq + self.secondary_line_ratio) + for v in np.arange(0, radius, step): + line1 = Line(start+v*unit, end+v*unit) + line2 = Line(start-v*unit, end-v*unit) + if v == 0: + self.axes.add(line1) + elif v in main_range: + self.main_lines.add(line1, line2) + else: + self.secondary_lines.add(line1, line2) + self.add(self.secondary_lines, self.main_lines, self.axes) + self.stretch(self.x_unit_size, 0) + self.stretch(self.y_unit_size, 1) + self.shift(self.center_point) + #Put x_axis before y_axis + y_axis, x_axis = self.axes.split() + self.axes = VMobject(x_axis, y_axis) + + def init_colors(self): + VMobject.init_colors(self) + self.axes.set_stroke(self.axes_color, self.stroke_width) + self.main_lines.set_stroke(self.color, self.stroke_width) + self.secondary_lines.set_stroke( + self.secondary_color, self.secondary_stroke_width + ) + return self + + def get_center_point(self): + return self.coords_to_point(0, 0) + + def coords_to_point(self, x, y): + x, y = np.array([x, y]) + result = self.axes.get_center() + result += x*self.get_x_unit_size()*RIGHT + result += y*self.get_y_unit_size()*UP + return result + + def point_to_coords(self, point): + new_point = point - self.axes.get_center() + x = new_point[0]/self.get_x_unit_size() + y = new_point[1]/self.get_y_unit_size() + return x, y + + # Does not recompute center, unit_sizes for each call; useful for + # iterating over large lists of points, but does assume these + # attributes are kept accurate. (Could alternatively have a method + # which returns a function dynamically created after a single + # call to each of get_center(), get_x_unit_size(), etc.) + def point_to_coords_cheap(self, point): + new_point = point - self.center_point + x = new_point[0]/self.x_unit_size + y = new_point[1]/self.y_unit_size + return x, y + + def get_x_unit_size(self): + return self.axes.get_width() / (2.0*self.x_radius) + + def get_y_unit_size(self): + return self.axes.get_height() / (2.0*self.y_radius) + + def get_coordinate_labels(self, x_vals = None, y_vals = None): + coordinate_labels = VGroup() + if x_vals == None: + x_vals = range(-int(self.x_radius), int(self.x_radius)+1) + if y_vals == None: + y_vals = range(-int(self.y_radius), int(self.y_radius)+1) + for index, vals in enumerate([x_vals, y_vals]): + num_pair = [0, 0] + for val in vals: + if val == 0: + continue + num_pair[index] = val + point = self.coords_to_point(*num_pair) + num = TexMobject(str(val)) + num.add_background_rectangle() + num.scale_to_fit_height( + self.written_coordinate_height + ) + num.next_to(point, DOWN+LEFT, buff = SMALL_BUFF) + coordinate_labels.add(num) + self.coordinate_labels = coordinate_labels + return coordinate_labels + + def get_axes(self): + return self.axes + + def get_axis_labels(self, x_label = "x", y_label = "y"): + x_axis, y_axis = self.get_axes().split() + quads = [ + (x_axis, x_label, UP, RIGHT), + (y_axis, y_label, RIGHT, UP), + ] + labels = VGroup() + for axis, tex, vect, edge in quads: + label = TexMobject(tex) + label.add_background_rectangle() + label.next_to(axis, vect) + label.to_edge(edge) + labels.add(label) + self.axis_labels = labels + return labels + + def add_coordinates(self, x_vals = None, y_vals = None): + self.add(*self.get_coordinate_labels(x_vals, y_vals)) + return self + + def get_vector(self, coords, **kwargs): + point = coords[0]*RIGHT + coords[1]*UP + arrow = Arrow(ORIGIN, coords, **kwargs) + return arrow + + def prepare_for_nonlinear_transform(self, num_inserted_anchor_points = 50): + for mob in self.family_members_with_points(): + num_anchors = mob.get_num_anchor_points() + if num_inserted_anchor_points > num_anchors: + mob.insert_n_anchor_points(num_inserted_anchor_points-num_anchors) + mob.make_smooth() + return self + +class ComplexPlane(NumberPlane): + CONFIG = { + "color" : BLUE, + "unit_size" : 1, + "line_frequency" : 1, + "faded_line_frequency" : 0.5, + } + def __init__(self, **kwargs): + digest_config(self, kwargs) + kwargs.update({ + "x_unit_size" : self.unit_size, + "y_unit_size" : self.unit_size, + "x_line_frequency" : self.line_frequency, + "x_faded_line_frequency" : self.faded_line_frequency, + "y_line_frequency" : self.line_frequency, + "y_faded_line_frequency" : self.faded_line_frequency, + }) + NumberPlane.__init__(self, **kwargs) + + def number_to_point(self, number): + number = complex(number) + return self.coords_to_point(number.real, number.imag) + + def point_to_number(self, point): + x, y = self.point_to_coords(point) + return complex(x, y) + + def get_coordinate_labels(self, *numbers): + # TODO: Should merge this with the code from NumberPlane.get_coordinate_labels + + result = VGroup() + nudge = 0.1*(DOWN+RIGHT) + if len(numbers) == 0: + numbers = range(-int(self.x_radius), int(self.x_radius)+1) + numbers += [ + complex(0, y) + for y in range(-int(self.y_radius), int(self.y_radius)+1) + ] + for number in numbers: + if number == complex(0, 0): + continue + point = self.number_to_point(number) + num_str = str(number).replace("j", "i") + if num_str.startswith("0"): + num_str = "0" + elif num_str in ["1i", "-1i"]: + num_str = num_str.replace("1", "") + num_mob = TexMobject(num_str) + num_mob.add_background_rectangle() + num_mob.scale_to_fit_height(self.written_coordinate_height) + num_mob.next_to(point, DOWN+LEFT, SMALL_BUFF) + result.add(num_mob) + self.coordinate_labels = result + return result + + def add_coordinates(self, *numbers): + self.add(*self.get_coordinate_labels(*numbers)) + return self + diff --git a/old_projects/fourier.py b/old_projects/fourier.py index 571bd341..830f8c78 100644 --- a/old_projects/fourier.py +++ b/old_projects/fourier.py @@ -4089,7 +4089,6 @@ class SubscribeOrBinge(PiCreatureScene): ) ) - class CloseWithAPuzzle(TeacherStudentsScene): def construct(self): self.teacher_says("Close with a puzzle!", run_time = 1) diff --git a/topics/complex_numbers.py b/topics/complex_numbers.py index 7d677b64..3d7370d2 100644 --- a/topics/complex_numbers.py +++ b/topics/complex_numbers.py @@ -2,15 +2,16 @@ from constants import * from animation.animation import Animation -from animation.movement import Homotopy from animation.creation import ShowCreation +from animation.movement import Homotopy from animation.movement import SmoothedVectorizedHomotopy from animation.transform import ApplyPointwiseFunction from animation.transform import MoveToTarget +from mobject.coordinate_systems import NumberPlane +from mobject.coordinate_systems import ComplexPlane from mobject.svg.tex_mobject import TexMobject from mobject.svg.tex_mobject import TextMobject from mobject.types.vectorized_mobject import VGroup -from number_line import NumberPlane from scene.scene import Scene from utils.config_ops import digest_config from utils.config_ops import instantiate @@ -171,81 +172,6 @@ class ComplexTransformationScene(Scene): def complex_string(complex_num): return filter(lambda c : c not in "()", str(complex_num)) -class ComplexPlane(NumberPlane): - CONFIG = { - "color" : BLUE, - "unit_size" : 1, - "line_frequency" : 1, - "faded_line_frequency" : 0.5, - } - def __init__(self, **kwargs): - digest_config(self, kwargs) - kwargs.update({ - "x_unit_size" : self.unit_size, - "y_unit_size" : self.unit_size, - "x_line_frequency" : self.line_frequency, - "x_faded_line_frequency" : self.faded_line_frequency, - "y_line_frequency" : self.line_frequency, - "y_faded_line_frequency" : self.faded_line_frequency, - }) - NumberPlane.__init__(self, **kwargs) - - def number_to_point(self, number): - number = complex(number) - return self.coords_to_point(number.real, number.imag) - - def point_to_number(self, point): - x, y = self.point_to_coords(point) - return complex(x, y) - - def get_coordinate_labels(self, *numbers): - # TODO: Should merge this with the code from NumberPlane.get_coordinate_labels - - result = VGroup() - nudge = 0.1*(DOWN+RIGHT) - if len(numbers) == 0: - numbers = range(-int(self.x_radius), int(self.x_radius)+1) - numbers += [ - complex(0, y) - for y in range(-int(self.y_radius), int(self.y_radius)+1) - ] - for number in numbers: - if number == complex(0, 0): - continue - point = self.number_to_point(number) - num_str = str(number).replace("j", "i") - if num_str.startswith("0"): - num_str = "0" - elif num_str in ["1i", "-1i"]: - num_str = num_str.replace("1", "") - num_mob = TexMobject(num_str) - num_mob.add_background_rectangle() - num_mob.scale_to_fit_height(self.written_coordinate_height) - num_mob.next_to(point, DOWN+LEFT, SMALL_BUFF) - result.add(num_mob) - self.coordinate_labels = result - return result - - def add_coordinates(self, *numbers): - self.add(*self.get_coordinate_labels(*numbers)) - return self - - def add_spider_web(self, circle_freq = 1, angle_freq = np.pi/6): - # This code no longer works because it has this reference to self.fade_factor - # which is never initialized. Shall we delete this little-used function entirely? - self.fade(self.fade_factor) - config = { - "color" : self.color, - "density" : self.density, - } - for radius in np.arange(circle_freq, FRAME_X_RADIUS, circle_freq): - self.add(Circle(radius = radius, **config)) - for angle in np.arange(0, 2*np.pi, angle_freq): - end_point = np.cos(angle)*RIGHT + np.sin(angle)*UP - end_point *= FRAME_X_RADIUS - self.add(Line(ORIGIN, end_point, **config)) - return self - class ComplexFunction(ApplyPointwiseFunction): def __init__(self, function, mobject = ComplexPlane, **kwargs): if "path_func" not in kwargs: diff --git a/topics/matrix.py b/topics/matrix.py index 985651a5..3aa60a0c 100644 --- a/topics/matrix.py +++ b/topics/matrix.py @@ -19,8 +19,8 @@ from mobject.geometry import Circle from mobject.geometry import Dot from mobject.geometry import Line from mobject.geometry import Vector -from topics.number_line import Axes -from topics.number_line import NumberPlane +from mobject.coordinate_systems import Axes +from mobject.coordinate_systems import NumberPlane from constants import * diff --git a/topics/number_line.py b/topics/number_line.py index c1e8f6cb..fc47d440 100644 --- a/topics/number_line.py +++ b/topics/number_line.py @@ -3,13 +3,10 @@ from constants import * from mobject.svg.tex_mobject import TexMobject from mobject.types.vectorized_mobject import VGroup from mobject.types.vectorized_mobject import VMobject -from scene.scene import Scene -from topics.functions import ParametricFunction from mobject.geometry import Arrow from mobject.geometry import Line from utils.bezier import interpolate from utils.config_ops import digest_config -from utils.space_ops import angle_of_vector class NumberLine(VMobject): CONFIG = { @@ -153,279 +150,6 @@ class UnitInterval(NumberLine): "number_at_center" : 0.5, } -class Axes(VGroup): - CONFIG = { - "propagate_style_to_family" : True, - "three_d" : False, - "number_line_config" : { - "color" : LIGHT_GREY, - "include_tip" : True, - }, - "x_axis_config" : {}, - "y_axis_config" : {}, - "z_axis_config" : {}, - "x_min" : -FRAME_X_RADIUS, - "x_max" : FRAME_X_RADIUS, - "y_min" : -FRAME_Y_RADIUS, - "y_max" : FRAME_Y_RADIUS, - "z_min" : -3.5, - "z_max" : 3.5, - "z_normal" : DOWN, - "default_num_graph_points" : 100, - } - def __init__(self, **kwargs): - VGroup.__init__(self, **kwargs) - self.x_axis = self.get_axis(self.x_min, self.x_max, self.x_axis_config) - self.y_axis = self.get_axis(self.y_min, self.y_max, self.y_axis_config) - self.y_axis.rotate(np.pi/2, about_point = ORIGIN) - self.add(self.x_axis, self.y_axis) - if self.three_d: - self.z_axis = self.get_axis(self.z_min, self.z_max, self.z_axis_config) - self.z_axis.rotate(-np.pi/2, UP, about_point = ORIGIN) - self.z_axis.rotate( - angle_of_vector(self.z_normal), OUT, - about_point = ORIGIN - ) - self.add(self.z_axis) - - def get_axis(self, min_val, max_val, extra_config): - config = dict(self.number_line_config) - config.update(extra_config) - return NumberLine(x_min = min_val, x_max = max_val, **config) - - def coords_to_point(self, x, y): - origin = self.x_axis.number_to_point(0) - x_axis_projection = self.x_axis.number_to_point(x) - y_axis_projection = self.y_axis.number_to_point(y) - return x_axis_projection + y_axis_projection - origin - - def point_to_coords(self, point): - return ( - self.x_axis.point_to_number(point), - self.y_axis.point_to_number(point), - ) - - def get_graph( - self, function, num_graph_points = None, - x_min = None, - x_max = None, - **kwargs - ): - kwargs["fill_opacity"] = kwargs.get("fill_opacity", 0) - kwargs["num_anchor_points"] = \ - num_graph_points or self.default_num_graph_points - x_min = x_min or self.x_min - x_max = x_max or self.x_max - graph = ParametricFunction( - lambda t : self.coords_to_point(t, function(t)), - t_min = x_min, - t_max = x_max, - **kwargs - ) - graph.underlying_function = function - return graph - - def input_to_graph_point(self, x, graph): - if hasattr(graph, "underlying_function"): - return self.coords_to_point(x, graph.underlying_function(x)) - else: - #binary search - lh, rh = 0, 1 - while abs(lh - rh) > 0.001: - mh = np.mean([lh, rh]) - hands = [lh, mh, rh] - points = map(graph.point_from_proportion, hands) - lx, mx, rx = map(self.x_axis.point_to_number, points) - if lx <= x and rx >= x: - if mx > x: - rh = mh - else: - lh = mh - elif lx <= x and rx <= x: - return points[2] - elif lx >= x and rx >= x: - return points[0] - elif lx > x and rx < x: - lh, rh = rh, lh - return points[1] - return self.coords_to_point(x, graph.underlying_function(x)) - -class ThreeDAxes(Axes): - CONFIG = { - "x_min" : -5.5, - "x_max" : 5.5, - "y_min" : -4.5, - "y_max" : 4.5, - "three_d" : True, - } - -class NumberPlane(VMobject): - CONFIG = { - "color" : BLUE_D, - "secondary_color" : BLUE_E, - "axes_color" : WHITE, - "secondary_stroke_width" : 1, - # TODO: Allow coordinate center of NumberPlane to not be at (0, 0) - "x_radius": None, - "y_radius": None, - "x_unit_size" : 1, - "y_unit_size" : 1, - "center_point" : ORIGIN, - "x_line_frequency" : 1, - "y_line_frequency" : 1, - "secondary_line_ratio" : 1, - "written_coordinate_height" : 0.2, - "propagate_style_to_family" : False, - "make_smooth_after_applying_functions" : True, - } - def generate_points(self): - if self.x_radius is None: - center_to_edge = (FRAME_X_RADIUS + abs(self.center_point[0])) - self.x_radius = center_to_edge / self.x_unit_size - if self.y_radius is None: - center_to_edge = (FRAME_Y_RADIUS + abs(self.center_point[1])) - self.y_radius = center_to_edge / self.y_unit_size - self.axes = VMobject() - self.main_lines = VMobject() - self.secondary_lines = VMobject() - tuples = [ - ( - self.x_radius, - self.x_line_frequency, - self.y_radius*DOWN, - self.y_radius*UP, - RIGHT - ), - ( - self.y_radius, - self.y_line_frequency, - self.x_radius*LEFT, - self.x_radius*RIGHT, - UP, - ), - ] - for radius, freq, start, end, unit in tuples: - main_range = np.arange(0, radius, freq) - step = freq/float(freq + self.secondary_line_ratio) - for v in np.arange(0, radius, step): - line1 = Line(start+v*unit, end+v*unit) - line2 = Line(start-v*unit, end-v*unit) - if v == 0: - self.axes.add(line1) - elif v in main_range: - self.main_lines.add(line1, line2) - else: - self.secondary_lines.add(line1, line2) - self.add(self.secondary_lines, self.main_lines, self.axes) - self.stretch(self.x_unit_size, 0) - self.stretch(self.y_unit_size, 1) - self.shift(self.center_point) - #Put x_axis before y_axis - y_axis, x_axis = self.axes.split() - self.axes = VMobject(x_axis, y_axis) - - def init_colors(self): - VMobject.init_colors(self) - self.axes.set_stroke(self.axes_color, self.stroke_width) - self.main_lines.set_stroke(self.color, self.stroke_width) - self.secondary_lines.set_stroke( - self.secondary_color, self.secondary_stroke_width - ) - return self - - def get_center_point(self): - return self.coords_to_point(0, 0) - - def coords_to_point(self, x, y): - x, y = np.array([x, y]) - result = self.axes.get_center() - result += x*self.get_x_unit_size()*RIGHT - result += y*self.get_y_unit_size()*UP - return result - - def point_to_coords(self, point): - new_point = point - self.axes.get_center() - x = new_point[0]/self.get_x_unit_size() - y = new_point[1]/self.get_y_unit_size() - return x, y - - # Does not recompute center, unit_sizes for each call; useful for - # iterating over large lists of points, but does assume these - # attributes are kept accurate. (Could alternatively have a method - # which returns a function dynamically created after a single - # call to each of get_center(), get_x_unit_size(), etc.) - def point_to_coords_cheap(self, point): - new_point = point - self.center_point - x = new_point[0]/self.x_unit_size - y = new_point[1]/self.y_unit_size - return x, y - - def get_x_unit_size(self): - return self.axes.get_width() / (2.0*self.x_radius) - - def get_y_unit_size(self): - return self.axes.get_height() / (2.0*self.y_radius) - - def get_coordinate_labels(self, x_vals = None, y_vals = None): - coordinate_labels = VGroup() - if x_vals == None: - x_vals = range(-int(self.x_radius), int(self.x_radius)+1) - if y_vals == None: - y_vals = range(-int(self.y_radius), int(self.y_radius)+1) - for index, vals in enumerate([x_vals, y_vals]): - num_pair = [0, 0] - for val in vals: - if val == 0: - continue - num_pair[index] = val - point = self.coords_to_point(*num_pair) - num = TexMobject(str(val)) - num.add_background_rectangle() - num.scale_to_fit_height( - self.written_coordinate_height - ) - num.next_to(point, DOWN+LEFT, buff = SMALL_BUFF) - coordinate_labels.add(num) - self.coordinate_labels = coordinate_labels - return coordinate_labels - - def get_axes(self): - return self.axes - - def get_axis_labels(self, x_label = "x", y_label = "y"): - x_axis, y_axis = self.get_axes().split() - quads = [ - (x_axis, x_label, UP, RIGHT), - (y_axis, y_label, RIGHT, UP), - ] - labels = VGroup() - for axis, tex, vect, edge in quads: - label = TexMobject(tex) - label.add_background_rectangle() - label.next_to(axis, vect) - label.to_edge(edge) - labels.add(label) - self.axis_labels = labels - return labels - - def add_coordinates(self, x_vals = None, y_vals = None): - self.add(*self.get_coordinate_labels(x_vals, y_vals)) - return self - - def get_vector(self, coords, **kwargs): - point = coords[0]*RIGHT + coords[1]*UP - arrow = Arrow(ORIGIN, coords, **kwargs) - return arrow - - def prepare_for_nonlinear_transform(self, num_inserted_anchor_points = 50): - for mob in self.family_members_with_points(): - num_anchors = mob.get_num_anchor_points() - if num_inserted_anchor_points > num_anchors: - mob.insert_n_anchor_points(num_inserted_anchor_points-num_anchors) - mob.make_smooth() - return self - - diff --git a/topics/vector_space_scene.py b/topics/vector_space_scene.py index 591eb966..e22cd90d 100644 --- a/topics/vector_space_scene.py +++ b/topics/vector_space_scene.py @@ -21,8 +21,8 @@ from mobject.geometry import Dot from mobject.geometry import Line from mobject.geometry import Square from mobject.geometry import Vector -from topics.number_line import Axes -from topics.number_line import NumberPlane +from mobject.coordinate_systems import Axes +from mobject.coordinate_systems import NumberPlane from constants import * from topics.matrix import Matrix From 267bfd52dd84024feac8f3e1d12967dca6d28b17 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Sat, 31 Mar 2018 18:49:28 -0700 Subject: [PATCH 10/17] Refactor topics/numerals into various numbers.py files in mobject/, animation/, and continual_animation/ --- animation/numbers.py | 61 ++++++++++++ big_ol_pile_of_manim_imports.py | 4 +- continual_animation/numbers.py | 10 ++ mobject/numbers.py | 85 ++++++++++++++++ topics/numerals.py | 169 -------------------------------- 5 files changed, 159 insertions(+), 170 deletions(-) create mode 100644 animation/numbers.py create mode 100644 continual_animation/numbers.py create mode 100644 mobject/numbers.py delete mode 100644 topics/numerals.py diff --git a/animation/numbers.py b/animation/numbers.py new file mode 100644 index 00000000..6fed7b26 --- /dev/null +++ b/animation/numbers.py @@ -0,0 +1,61 @@ +from __future__ import absolute_import + +from constants import * + +from animation.animation import Animation +from mobject.numbers import DecimalNumber +from utils.bezier import interpolate +from utils.config_ops import digest_config + +class ChangingDecimal(Animation): + CONFIG = { + "num_decimal_points" : None, + "show_ellipsis" : None, + "position_update_func" : None, + "tracked_mobject" : None, + } + def __init__(self, decimal_number_mobject, number_update_func, **kwargs): + digest_config(self, kwargs, locals()) + self.decimal_number_config = dict( + decimal_number_mobject.initial_config + ) + for attr in "num_decimal_points", "show_ellipsis": + value = getattr(self, attr) + if value is not None: + self.decimal_number_config[attr] = value + if hasattr(self.decimal_number_mobject, "background_rectangle"): + self.decimal_number_config["include_background_rectangle"] = True + if self.tracked_mobject: + dmc = decimal_number_mobject.get_center() + tmc = self.tracked_mobject.get_center() + self.diff_from_tracked_mobject = dmc - tmc + Animation.__init__(self, decimal_number_mobject, **kwargs) + + def update_mobject(self, alpha): + self.update_number(alpha) + self.update_position() + + def update_number(self, alpha): + decimal = self.decimal_number_mobject + new_number = self.number_update_func(alpha) + new_decimal = DecimalNumber( + new_number, **self.decimal_number_config + ) + new_decimal.match_height(decimal) + new_decimal.move_to(decimal) + new_decimal.match_style(decimal) + + decimal.submobjects = new_decimal.submobjects + decimal.number = new_number + + def update_position(self): + if self.position_update_func is not None: + self.position_update_func(self.decimal_number_mobject) + elif self.tracked_mobject is not None: + self.decimal_number_mobject.move_to(self.tracked_mobject.get_center() + self.diff_from_tracked_mobject) + +class ChangeDecimalToValue(ChangingDecimal): + def __init__(self, decimal_number_mobject, target_number, **kwargs): + start_number = decimal_number_mobject.number + func = lambda alpha : interpolate(start_number, target_number, alpha) + ChangingDecimal.__init__(self, decimal_number_mobject, func, **kwargs) diff --git a/big_ol_pile_of_manim_imports.py b/big_ol_pile_of_manim_imports.py index bb1ef166..06de7344 100644 --- a/big_ol_pile_of_manim_imports.py +++ b/big_ol_pile_of_manim_imports.py @@ -20,6 +20,7 @@ from animation.composition import * from animation.creation import * from animation.indication import * from animation.movement import * +from animation.numbers import * from animation.rotation import * from animation.specialized import * from animation.transform import * @@ -31,11 +32,13 @@ from camera.moving_camera import * from continual_animation.continual_animation import * from continual_animation.from_animation import * +from continual_animation.numbers import * from continual_animation.update import * from mobject.mobject import * from mobject.frame import * from mobject.geometry import * +from mobject.numbers import * from mobject.shape_matchers import * from mobject.value_tracker import * from mobject.svg.brace import * @@ -68,7 +71,6 @@ from topics.functions import * from topics.graph_scene import * from topics.matrix import * from topics.number_line import * -from topics.numerals import * from topics.probability import * from topics.three_dimensions import * from topics.vector_space_scene import * diff --git a/continual_animation/numbers.py b/continual_animation/numbers.py new file mode 100644 index 00000000..42679af6 --- /dev/null +++ b/continual_animation/numbers.py @@ -0,0 +1,10 @@ +from __future__ import absolute_import + +from continual_animation.from_animation import NormalAnimationAsContinualAnimation +from animation.numbers import ChangingDecimal + +class ContinualChangingDecimal(NormalAnimationAsContinualAnimation): + def __init__(self, *args, **kwargs): + NormalAnimationAsContinualAnimation.__init__( + self, ChangingDecimal(*args, **kwargs) + ) diff --git a/mobject/numbers.py b/mobject/numbers.py new file mode 100644 index 00000000..bb50c070 --- /dev/null +++ b/mobject/numbers.py @@ -0,0 +1,85 @@ +from __future__ import absolute_import + +from constants import * + +from mobject.svg.tex_mobject import TexMobject +from mobject.types.vectorized_mobject import VGroup +from mobject.types.vectorized_mobject import VMobject +from mobject.shape_matchers import BackgroundRectangle + +class DecimalNumber(VMobject): + CONFIG = { + "num_decimal_points" : 2, + "digit_to_digit_buff" : 0.05, + "show_ellipsis" : False, + "unit" : None, #Aligned to bottom unless it starts with "^" + "include_background_rectangle" : False, + } + def __init__(self, number, **kwargs): + VMobject.__init__(self, **kwargs) + self.number = number + ndp = self.num_decimal_points + + #Build number string + if isinstance(number, complex): + num_string = '%.*f%s%.*fi'%( + ndp, number.real, + "-" if number.imag < 0 else "+", + ndp, abs(number.imag) + ) + else: + num_string = '%.*f'%(ndp, number) + negative_zero_string = "-%.*f"%(ndp, 0.) + if num_string == negative_zero_string: + num_string = num_string[1:] + self.add(*[ + TexMobject(char, **kwargs) + for char in num_string + ]) + + #Add non-numerical bits + if self.show_ellipsis: + self.add(TexMobject("\\dots")) + + + if num_string.startswith("-"): + minus = self.submobjects[0] + minus.next_to( + self.submobjects[1], LEFT, + buff = self.digit_to_digit_buff + ) + + if self.unit != None: + self.unit_sign = TexMobject(self.unit) + self.add(self.unit_sign) + + self.arrange_submobjects( + buff = self.digit_to_digit_buff, + aligned_edge = DOWN + ) + + #Handle alignment of parts that should be aligned + #to the bottom + for i, c in enumerate(num_string): + if c == "-" and len(num_string) > i+1: + self[i].align_to(self[i+1], alignment_vect = UP) + if self.unit and self.unit.startswith("^"): + self.unit_sign.align_to(self, UP) + # + if self.include_background_rectangle: + self.add_background_rectangle() + + def add_background_rectangle(self): + #TODO, is this the best way to handle + #background rectangles? + self.background_rectangle = BackgroundRectangle(self) + self.submobjects = [ + self.background_rectangle, + VGroup(*self.submobjects) + ] + return self + +class Integer(DecimalNumber): + CONFIG = { + "num_decimal_points" : 0, + } diff --git a/topics/numerals.py b/topics/numerals.py deleted file mode 100644 index 0f8bc901..00000000 --- a/topics/numerals.py +++ /dev/null @@ -1,169 +0,0 @@ - -from animation.animation import Animation -from constants import * -from continual_animation.continual_animation import ContinualAnimation -from mobject.svg.tex_mobject import TexMobject -from mobject.types.vectorized_mobject import VGroup -from mobject.types.vectorized_mobject import VMobject -from mobject.types.vectorized_mobject import VectorizedPoint -from scene.scene import Scene -from mobject.shape_matchers import BackgroundRectangle -from utils.bezier import interpolate -from utils.config_ops import digest_config - -class DecimalNumber(VMobject): - CONFIG = { - "num_decimal_points" : 2, - "digit_to_digit_buff" : 0.05, - "show_ellipsis" : False, - "unit" : None, #Aligned to bottom unless it starts with "^" - "include_background_rectangle" : False, - } - def __init__(self, number, **kwargs): - VMobject.__init__(self, **kwargs) - self.number = number - ndp = self.num_decimal_points - - #Build number string - if isinstance(number, complex): - num_string = '%.*f%s%.*fi'%( - ndp, number.real, - "-" if number.imag < 0 else "+", - ndp, abs(number.imag) - ) - else: - num_string = '%.*f'%(ndp, number) - negative_zero_string = "-%.*f"%(ndp, 0.) - if num_string == negative_zero_string: - num_string = num_string[1:] - self.add(*[ - TexMobject(char, **kwargs) - for char in num_string - ]) - - #Add non-numerical bits - if self.show_ellipsis: - self.add(TexMobject("\\dots")) - - - if num_string.startswith("-"): - minus = self.submobjects[0] - minus.next_to( - self.submobjects[1], LEFT, - buff = self.digit_to_digit_buff - ) - - if self.unit != None: - self.unit_sign = TexMobject(self.unit) - self.add(self.unit_sign) - - self.arrange_submobjects( - buff = self.digit_to_digit_buff, - aligned_edge = DOWN - ) - - #Handle alignment of parts that should be aligned - #to the bottom - for i, c in enumerate(num_string): - if c == "-" and len(num_string) > i+1: - self[i].align_to(self[i+1], alignment_vect = UP) - if self.unit and self.unit.startswith("^"): - self.unit_sign.align_to(self, UP) - # - if self.include_background_rectangle: - self.add_background_rectangle() - - def add_background_rectangle(self): - #TODO, is this the best way to handle - #background rectangles? - self.background_rectangle = BackgroundRectangle(self) - self.submobjects = [ - self.background_rectangle, - VGroup(*self.submobjects) - ] - return self - -class Integer(DecimalNumber): - CONFIG = { - "num_decimal_points" : 0, - } - -class ChangingDecimal(Animation): - CONFIG = { - "num_decimal_points" : None, - "show_ellipsis" : None, - "position_update_func" : None, - "tracked_mobject" : None, - } - def __init__(self, decimal_number_mobject, number_update_func, **kwargs): - digest_config(self, kwargs, locals()) - self.decimal_number_config = dict( - decimal_number_mobject.initial_config - ) - for attr in "num_decimal_points", "show_ellipsis": - value = getattr(self, attr) - if value is not None: - self.decimal_number_config[attr] = value - if hasattr(self.decimal_number_mobject, "background_rectangle"): - self.decimal_number_config["include_background_rectangle"] = True - if self.tracked_mobject: - dmc = decimal_number_mobject.get_center() - tmc = self.tracked_mobject.get_center() - self.diff_from_tracked_mobject = dmc - tmc - Animation.__init__(self, decimal_number_mobject, **kwargs) - - def update_mobject(self, alpha): - self.update_number(alpha) - self.update_position() - - def update_number(self, alpha): - decimal = self.decimal_number_mobject - new_number = self.number_update_func(alpha) - new_decimal = DecimalNumber( - new_number, **self.decimal_number_config - ) - new_decimal.match_height(decimal) - new_decimal.move_to(decimal) - new_decimal.match_style(decimal) - - decimal.submobjects = new_decimal.submobjects - decimal.number = new_number - - def update_position(self): - if self.position_update_func is not None: - self.position_update_func(self.decimal_number_mobject) - elif self.tracked_mobject is not None: - self.decimal_number_mobject.move_to(self.tracked_mobject.get_center() + self.diff_from_tracked_mobject) - -class ChangeDecimalToValue(ChangingDecimal): - def __init__(self, decimal_number_mobject, target_number, **kwargs): - start_number = decimal_number_mobject.number - func = lambda alpha : interpolate(start_number, target_number, alpha) - ChangingDecimal.__init__(self, decimal_number_mobject, func, **kwargs) - -class ContinualChangingDecimal(ContinualAnimation): - def __init__(self, decimal_number_mobject, number_update_func, **kwargs): - self.anim = ChangingDecimal(decimal_number_mobject, number_update_func, **kwargs) - ContinualAnimation.__init__(self, decimal_number_mobject, **kwargs) - - def update_mobject(self, dt): - self.anim.update(self.internal_time) - - - - - - - - - - - - - - - - - - - From 73514a2d778745630d07291aa5c96a52c23bfed5 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Sat, 31 Mar 2018 18:57:21 -0700 Subject: [PATCH 11/17] Factored out topics/complex_numbers constructs to the appropriate places with mobject/, animation/ and scene/ --- animation/movement.py | 12 ++++++ animation/transform.py | 17 ++++++++ big_ol_pile_of_manim_imports.py | 2 +- .../complex_transformation_scene.py | 40 ------------------- utils/strings.py | 5 ++- 5 files changed, 34 insertions(+), 42 deletions(-) rename topics/complex_numbers.py => once_useful_constructs/complex_transformation_scene.py (79%) diff --git a/animation/movement.py b/animation/movement.py index 8842240f..201ed779 100644 --- a/animation/movement.py +++ b/animation/movement.py @@ -34,6 +34,18 @@ class SmoothedVectorizedHomotopy(Homotopy): Homotopy.update_submobject(self, submob, start, alpha) submob.make_smooth() +class ComplexHomotopy(Homotopy): + def __init__(self, complex_homotopy, mobject, **kwargs): + """ + Complex Hootopy a function Cx[0, 1] to C + """ + def homotopy(event): + x, y, z, t = event + c = complex_homotopy((complex(x, y), t)) + return (c.real, c.imag, z) + Homotopy.__init__(self, homotopy, mobject, *args, **kwargs) + + class PhaseFlow(Animation): CONFIG = { "virtual_time" : 1, diff --git a/animation/transform.py b/animation/transform.py index 38778c28..c684e91e 100644 --- a/animation/transform.py +++ b/animation/transform.py @@ -12,8 +12,10 @@ from utils.config_ops import digest_config from utils.iterables import adjacent_pairs from utils.paths import path_along_arc from utils.paths import straight_path +from utils.config_ops import instantiate from utils.rate_functions import smooth from utils.rate_functions import squish_rate_func +from utils.space_ops import complex_to_R3 class Transform(Animation): CONFIG = { @@ -162,6 +164,21 @@ class ApplyMatrix(ApplyPointwiseFunction): return np.dot(p, transpose) ApplyPointwiseFunction.__init__(self, func, mobject, **kwargs) +class ComplexFunction(ApplyPointwiseFunction): + def __init__(self, function, mobject, **kwargs): + if "path_func" not in kwargs: + self.path_func = path_along_arc( + np.log(function(complex(1))).imag + ) + ApplyPointwiseFunction.__init__( + self, + lambda (x, y, z) : complex_to_R3(function(complex(x, y))), + instantiate(mobject), + **kwargs + ) + +### + class CyclicReplace(Transform): CONFIG = { "path_arc" : np.pi/2 diff --git a/big_ol_pile_of_manim_imports.py b/big_ol_pile_of_manim_imports.py index 06de7344..88420935 100644 --- a/big_ol_pile_of_manim_imports.py +++ b/big_ol_pile_of_manim_imports.py @@ -61,12 +61,12 @@ from scene.zoomed_scene import * from once_useful_constructs.arithmetic import * from once_useful_constructs.combinatorics import * +from once_useful_constructs.complex_transformation_scene import * from once_useful_constructs.counting import * from once_useful_constructs.fractals import * from once_useful_constructs.graph_theory import * from once_useful_constructs.light import * -from topics.complex_numbers import * from topics.functions import * from topics.graph_scene import * from topics.matrix import * diff --git a/topics/complex_numbers.py b/once_useful_constructs/complex_transformation_scene.py similarity index 79% rename from topics/complex_numbers.py rename to once_useful_constructs/complex_transformation_scene.py index 3d7370d2..efbbe21b 100644 --- a/topics/complex_numbers.py +++ b/once_useful_constructs/complex_transformation_scene.py @@ -1,23 +1,12 @@ from constants import * - from animation.animation import Animation -from animation.creation import ShowCreation -from animation.movement import Homotopy from animation.movement import SmoothedVectorizedHomotopy from animation.transform import ApplyPointwiseFunction from animation.transform import MoveToTarget -from mobject.coordinate_systems import NumberPlane from mobject.coordinate_systems import ComplexPlane -from mobject.svg.tex_mobject import TexMobject -from mobject.svg.tex_mobject import TextMobject from mobject.types.vectorized_mobject import VGroup from scene.scene import Scene -from utils.config_ops import digest_config -from utils.config_ops import instantiate -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 = { @@ -167,35 +156,6 @@ class ComplexTransformationScene(Scene): *added_anims ) -##### Unsure about what comes under here... - -def complex_string(complex_num): - return filter(lambda c : c not in "()", str(complex_num)) - -class ComplexFunction(ApplyPointwiseFunction): - def __init__(self, function, mobject = ComplexPlane, **kwargs): - if "path_func" not in kwargs: - self.path_func = path_along_arc( - np.log(function(complex(1))).imag - ) - ApplyPointwiseFunction.__init__( - self, - lambda (x, y, z) : complex_to_R3(function(complex(x, y))), - instantiate(mobject), - **kwargs - ) - -class ComplexHomotopy(Homotopy): - def __init__(self, complex_homotopy, mobject = ComplexPlane, **kwargs): - """ - Complex Hootopy a function Cx[0, 1] to C - """ - def homotopy(event): - x, y, z, t = event - c = complex_homotopy((complex(x, y), t)) - return (c.real, c.imag, z) - Homotopy.__init__(self, homotopy, mobject, *args, **kwargs) - diff --git a/utils/strings.py b/utils/strings.py index bfed3b66..54eaae3e 100644 --- a/utils/strings.py +++ b/utils/strings.py @@ -16,4 +16,7 @@ def initials(name, sep_values = [" ", "_"]): ]) def camel_case_initials(name): - return filter(lambda c : c.isupper(), name) \ No newline at end of file + return filter(lambda c : c.isupper(), name) + +def complex_string(complex_num): + return filter(lambda c : c not in "()", str(complex_num)) From 78e37b0f7c288739aa7f481006faf2c64914c4cf Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Sat, 31 Mar 2018 19:00:26 -0700 Subject: [PATCH 12/17] moved number_line.py to mobject/ --- big_ol_pile_of_manim_imports.py | 9 ++++----- mobject/coordinate_systems.py | 4 ++-- {topics => mobject}/number_line.py | 9 --------- topics/graph_scene.py | 10 +++++----- 4 files changed, 11 insertions(+), 21 deletions(-) rename {topics => mobject}/number_line.py (99%) diff --git a/big_ol_pile_of_manim_imports.py b/big_ol_pile_of_manim_imports.py index 88420935..af2f7be6 100644 --- a/big_ol_pile_of_manim_imports.py +++ b/big_ol_pile_of_manim_imports.py @@ -35,23 +35,24 @@ from continual_animation.from_animation import * from continual_animation.numbers import * from continual_animation.update import * -from mobject.mobject import * from mobject.frame import * from mobject.geometry import * +from mobject.mobject import * +from mobject.number_line import * from mobject.numbers import * from mobject.shape_matchers import * -from mobject.value_tracker import * from mobject.svg.brace import * from mobject.svg.svg_mobject import * from mobject.svg.tex_mobject import * from mobject.types.image_mobject import * from mobject.types.point_cloud_mobject import * from mobject.types.vectorized_mobject import * +from mobject.value_tracker import * +from for_3b1b_videos.common_scenes import * from for_3b1b_videos.pi_creature import * from for_3b1b_videos.pi_creature_animations import * from for_3b1b_videos.pi_creature_scene import * -from for_3b1b_videos.common_scenes import * from scene.moving_camera_scene import * from scene.reconfigurable_scene import * @@ -70,7 +71,6 @@ from once_useful_constructs.light import * from topics.functions import * from topics.graph_scene import * from topics.matrix import * -from topics.number_line import * from topics.probability import * from topics.three_dimensions import * from topics.vector_space_scene import * @@ -101,4 +101,3 @@ import sys from PIL import Image from colour import Color - diff --git a/mobject/coordinate_systems.py b/mobject/coordinate_systems.py index 0b92a04c..9a634ef8 100644 --- a/mobject/coordinate_systems.py +++ b/mobject/coordinate_systems.py @@ -6,14 +6,14 @@ from constants import * from mobject.geometry import Arrow from mobject.geometry import Line +from mobject.number_line import NumberLine from mobject.svg.tex_mobject import TexMobject from mobject.types.vectorized_mobject import VGroup from mobject.types.vectorized_mobject import VMobject from topics.functions import ParametricFunction -from topics.number_line import NumberLine from utils.config_ops import digest_config -from utils.space_ops import angle_of_vector from utils.space_ops import R3_to_complex +from utils.space_ops import angle_of_vector from utils.space_ops import complex_to_R3 #TODO: There should be much more code reuse between Axes, NumberPlane and GraphScene diff --git a/topics/number_line.py b/mobject/number_line.py similarity index 99% rename from topics/number_line.py rename to mobject/number_line.py index fc47d440..5022df21 100644 --- a/topics/number_line.py +++ b/mobject/number_line.py @@ -150,12 +150,3 @@ class UnitInterval(NumberLine): "number_at_center" : 0.5, } - - - - - - - - - diff --git a/topics/graph_scene.py b/topics/graph_scene.py index 7dbe5eca..403e96b6 100644 --- a/topics/graph_scene.py +++ b/topics/graph_scene.py @@ -3,18 +3,18 @@ from constants import * from scene.scene import Scene # from mobject.geometry import from animation.creation import ShowCreation -from animation.update import UpdateFromAlphaFunc from animation.creation import Write from animation.transform import Transform +from animation.update import UpdateFromAlphaFunc +from mobject.geometry import DashedLine +from mobject.geometry import Line +from mobject.geometry import Rectangle +from mobject.number_line import NumberLine from mobject.svg.tex_mobject import TexMobject from mobject.svg.tex_mobject import TextMobject from mobject.types.vectorized_mobject import VGroup from mobject.types.vectorized_mobject import VectorizedPoint from topics.functions import ParametricFunction -from mobject.geometry import DashedLine -from mobject.geometry import Line -from mobject.geometry import Rectangle -from topics.number_line import NumberLine from utils.bezier import interpolate from utils.color import color_gradient from utils.color import invert_color From b37f21234950d60b3a6af5807f0aecee815d1843 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Sat, 31 Mar 2018 19:05:53 -0700 Subject: [PATCH 13/17] Moved topics/functions.py to mobject/, and moved topics/graph_scene.py to scene --- big_ol_pile_of_manim_imports.py | 4 ++-- mobject/coordinate_systems.py | 2 +- {topics => mobject}/functions.py | 5 +++-- {topics => scene}/graph_scene.py | 7 +++---- 4 files changed, 9 insertions(+), 9 deletions(-) rename {topics => mobject}/functions.py (97%) rename {topics => scene}/graph_scene.py (98%) diff --git a/big_ol_pile_of_manim_imports.py b/big_ol_pile_of_manim_imports.py index af2f7be6..5c7a0f75 100644 --- a/big_ol_pile_of_manim_imports.py +++ b/big_ol_pile_of_manim_imports.py @@ -36,6 +36,7 @@ from continual_animation.numbers import * from continual_animation.update import * from mobject.frame import * +from mobject.functions import * from mobject.geometry import * from mobject.mobject import * from mobject.number_line import * @@ -54,6 +55,7 @@ from for_3b1b_videos.pi_creature import * from for_3b1b_videos.pi_creature_animations import * from for_3b1b_videos.pi_creature_scene import * +from scene.graph_scene import * from scene.moving_camera_scene import * from scene.reconfigurable_scene import * from scene.scene import * @@ -68,8 +70,6 @@ from once_useful_constructs.fractals import * from once_useful_constructs.graph_theory import * from once_useful_constructs.light import * -from topics.functions import * -from topics.graph_scene import * from topics.matrix import * from topics.probability import * from topics.three_dimensions import * diff --git a/mobject/coordinate_systems.py b/mobject/coordinate_systems.py index 9a634ef8..ece9e3d4 100644 --- a/mobject/coordinate_systems.py +++ b/mobject/coordinate_systems.py @@ -4,13 +4,13 @@ import numpy as np from constants import * +from mobject.functions import ParametricFunction from mobject.geometry import Arrow from mobject.geometry import Line from mobject.number_line import NumberLine from mobject.svg.tex_mobject import TexMobject from mobject.types.vectorized_mobject import VGroup from mobject.types.vectorized_mobject import VMobject -from topics.functions import ParametricFunction from utils.config_ops import digest_config from utils.space_ops import R3_to_complex from utils.space_ops import angle_of_vector diff --git a/topics/functions.py b/mobject/functions.py similarity index 97% rename from topics/functions.py rename to mobject/functions.py index ddf94bf5..0d421e66 100644 --- a/topics/functions.py +++ b/mobject/functions.py @@ -1,9 +1,10 @@ -from scipy import integrate +from __future__ import absolute_import + +from constants import * from mobject.types.vectorized_mobject import VMobject from utils.config_ops import digest_config -from constants import * class ParametricFunction(VMobject): CONFIG = { diff --git a/topics/graph_scene.py b/scene/graph_scene.py similarity index 98% rename from topics/graph_scene.py rename to scene/graph_scene.py index 403e96b6..e7fe94f8 100644 --- a/topics/graph_scene.py +++ b/scene/graph_scene.py @@ -1,12 +1,12 @@ +from __future__ import absolute_import + from constants import * from scene.scene import Scene -# from mobject.geometry import -from animation.creation import ShowCreation from animation.creation import Write from animation.transform import Transform from animation.update import UpdateFromAlphaFunc -from mobject.geometry import DashedLine +from mobject.functions import ParametricFunction from mobject.geometry import Line from mobject.geometry import Rectangle from mobject.number_line import NumberLine @@ -14,7 +14,6 @@ from mobject.svg.tex_mobject import TexMobject from mobject.svg.tex_mobject import TextMobject from mobject.types.vectorized_mobject import VGroup from mobject.types.vectorized_mobject import VectorizedPoint -from topics.functions import ParametricFunction from utils.bezier import interpolate from utils.color import color_gradient from utils.color import invert_color From c82417e008db52823813db48d7d04c642031e88d Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Sun, 1 Apr 2018 10:21:42 -0700 Subject: [PATCH 14/17] Moved moved matrix.py to mobject, and pulled out MatrixMultiplicationScene --- big_ol_pile_of_manim_imports.py | 2 +- extract_scene.py | 1 - mobject/matrix.py | 135 +++++++++ mobject/number_line.py | 2 + .../matrix_multiplication.py | 140 +++++++++ scene/graph_scene.py | 3 + topics/matrix.py | 277 ------------------ topics/vector_space_scene.py | 6 +- 8 files changed, 284 insertions(+), 282 deletions(-) create mode 100644 mobject/matrix.py create mode 100644 once_useful_constructs/matrix_multiplication.py delete mode 100644 topics/matrix.py diff --git a/big_ol_pile_of_manim_imports.py b/big_ol_pile_of_manim_imports.py index 5c7a0f75..72f7711b 100644 --- a/big_ol_pile_of_manim_imports.py +++ b/big_ol_pile_of_manim_imports.py @@ -70,7 +70,7 @@ from once_useful_constructs.fractals import * from once_useful_constructs.graph_theory import * from once_useful_constructs.light import * -from topics.matrix import * +from mobject.matrix import * from topics.probability import * from topics.three_dimensions import * from topics.vector_space_scene import * diff --git a/extract_scene.py b/extract_scene.py index 6e43b632..32766b46 100644 --- a/extract_scene.py +++ b/extract_scene.py @@ -216,7 +216,6 @@ def get_module(file_name): return get_module_windows(file_name) return get_module_posix(file_name) - def main(): config = get_configuration() module = get_module(config["file"]) diff --git a/mobject/matrix.py b/mobject/matrix.py new file mode 100644 index 00000000..279a076c --- /dev/null +++ b/mobject/matrix.py @@ -0,0 +1,135 @@ +import numpy as np + +from mobject.mobject import Mobject +from mobject.svg.tex_mobject import TexMobject +from mobject.types.vectorized_mobject import VGroup +from mobject.types.vectorized_mobject import VMobject +from mobject.shape_matchers import BackgroundRectangle + +from constants import * + +VECTOR_LABEL_SCALE_FACTOR = 0.8 + +def matrix_to_tex_string(matrix): + matrix = np.array(matrix).astype("string") + if matrix.ndim == 1: + matrix = matrix.reshape((matrix.size, 1)) + n_rows, n_cols = matrix.shape + prefix = "\\left[ \\begin{array}{%s}"%("c"*n_cols) + suffix = "\\end{array} \\right]" + rows = [ + " & ".join(row) + for row in matrix + ] + return prefix + " \\\\ ".join(rows) + suffix + +def matrix_to_mobject(matrix): + return TexMobject(matrix_to_tex_string(matrix)) + +def vector_coordinate_label(vector_mob, integer_labels = True, + n_dim = 2, color = WHITE): + vect = np.array(vector_mob.get_end()) + if integer_labels: + vect = np.round(vect).astype(int) + vect = vect[:n_dim] + vect = vect.reshape((n_dim, 1)) + label = Matrix(vect, add_background_rectangles = True) + label.scale(VECTOR_LABEL_SCALE_FACTOR) + + shift_dir = np.array(vector_mob.get_end()) + if shift_dir[0] >= 0: #Pointing right + shift_dir -= label.get_left() + DEFAULT_MOBJECT_TO_MOBJECT_BUFFER*LEFT + else: #Pointing left + shift_dir -= label.get_right() + DEFAULT_MOBJECT_TO_MOBJECT_BUFFER*RIGHT + label.shift(shift_dir) + label.set_color(color) + label.rect = BackgroundRectangle(label) + label.add_to_back(label.rect) + return label + +class Matrix(VMobject): + CONFIG = { + "v_buff" : 0.5, + "h_buff" : 1, + "add_background_rectangles" : False + } + def __init__(self, matrix, **kwargs): + """ + Matrix can either either include numbres, tex_strings, + or mobjects + """ + VMobject.__init__(self, **kwargs) + matrix = np.array(matrix) + if matrix.ndim == 1: + matrix = matrix.reshape((matrix.size, 1)) + if not isinstance(matrix[0][0], Mobject): + matrix = matrix.astype("string") + matrix = self.string_matrix_to_mob_matrix(matrix) + self.organize_mob_matrix(matrix) + self.add(*matrix.flatten()) + self.add_brackets() + self.center() + self.mob_matrix = matrix + if self.add_background_rectangles: + for mob in matrix.flatten(): + mob.add_background_rectangle() + + def string_matrix_to_mob_matrix(self, matrix): + return np.array([ + map(TexMobject, row) + for row in matrix + ]).reshape(matrix.shape) + + def organize_mob_matrix(self, matrix): + for i, row in enumerate(matrix): + for j, elem in enumerate(row): + mob = matrix[i][j] + if i == 0 and j == 0: + continue + elif i == 0: + mob.next_to(matrix[i][j-1], RIGHT, self.h_buff) + else: + mob.next_to(matrix[i-1][j], DOWN, self.v_buff) + return self + + def add_brackets(self): + bracket_pair = TexMobject("\\big[ \\big]") + bracket_pair.scale(2) + bracket_pair.stretch_to_fit_height(self.get_height() + 0.5) + l_bracket, r_bracket = bracket_pair.split() + l_bracket.next_to(self, LEFT) + r_bracket.next_to(self, RIGHT) + self.add(l_bracket, r_bracket) + self.brackets = VGroup(l_bracket, r_bracket) + return self + + def set_color_columns(self, *colors): + for i, color in enumerate(colors): + VGroup(*self.mob_matrix[:,i]).set_color(color) + return self + + def add_background_to_entries(self): + for mob in self.get_entries(): + mob.add_background_rectangle() + return self + + def get_mob_matrix(self): + return self.mob_matrix + + def get_entries(self): + return VGroup(*self.get_mob_matrix().flatten()) + + def get_brackets(self): + return self.brackets + + + + + + + + + + + + diff --git a/mobject/number_line.py b/mobject/number_line.py index 5022df21..ba332f51 100644 --- a/mobject/number_line.py +++ b/mobject/number_line.py @@ -1,3 +1,5 @@ +from __future__ import absolute_import + from constants import * from mobject.svg.tex_mobject import TexMobject diff --git a/once_useful_constructs/matrix_multiplication.py b/once_useful_constructs/matrix_multiplication.py new file mode 100644 index 00000000..a7067cb5 --- /dev/null +++ b/once_useful_constructs/matrix_multiplication.py @@ -0,0 +1,140 @@ +import numpy as np + +from animation.creation import ShowCreation +from animation.transform import ApplyMethod +from animation.creation import FadeOut +from animation.transform import Transform +from mobject.matrix import Matrix +from mobject.svg.tex_mobject import TexMobject +from mobject.types.vectorized_mobject import VGroup +from scene.scene import Scene +from mobject.geometry import Circle +from mobject.geometry import Line + +class NumericalMatrixMultiplication(Scene): + CONFIG = { + "left_matrix" : [[1, 2], [3, 4]], + "right_matrix" : [[5, 6], [7, 8]], + "use_parens" : True, + } + def construct(self): + left_string_matrix, right_string_matrix = [ + np.array(matrix).astype("string") + for matrix in self.left_matrix, self.right_matrix + ] + if right_string_matrix.shape[0] != left_string_matrix.shape[1]: + raise Exception("Incompatible shapes for matrix multiplication") + + left = Matrix(left_string_matrix) + right = Matrix(right_string_matrix) + result = self.get_result_matrix( + left_string_matrix, right_string_matrix + ) + + self.organize_matrices(left, right, result) + self.animate_product(left, right, result) + + + def get_result_matrix(self, left, right): + (m, k), n = left.shape, right.shape[1] + mob_matrix = np.array([VGroup()]).repeat(m*n).reshape((m, n)) + for a in range(m): + for b in range(n): + template = "(%s)(%s)" if self.use_parens else "%s%s" + parts = [ + prefix + template%(left[a][c], right[c][b]) + for c in range(k) + for prefix in ["" if c == 0 else "+"] + ] + mob_matrix[a][b] = TexMobject(parts, next_to_buff = 0.1) + return Matrix(mob_matrix) + + def add_lines(self, left, right): + line_kwargs = { + "color" : BLUE, + "stroke_width" : 2, + } + left_rows = [ + VGroup(*row) for row in left.get_mob_matrix() + ] + h_lines = VGroup() + for row in left_rows[:-1]: + h_line = Line(row.get_left(), row.get_right(), **line_kwargs) + h_line.next_to(row, DOWN, buff = left.v_buff/2.) + h_lines.add(h_line) + + right_cols = [ + VGroup(*col) for col in np.transpose(right.get_mob_matrix()) + ] + v_lines = VGroup() + for col in right_cols[:-1]: + v_line = Line(col.get_top(), col.get_bottom(), **line_kwargs) + v_line.next_to(col, RIGHT, buff = right.h_buff/2.) + v_lines.add(v_line) + + self.play(ShowCreation(h_lines)) + self.play(ShowCreation(v_lines)) + self.wait() + self.show_frame() + + def organize_matrices(self, left, right, result): + equals = TexMobject("=") + everything = VGroup(left, right, equals, result) + everything.arrange_submobjects() + everything.scale_to_fit_width(FRAME_WIDTH-1) + self.add(everything) + + + def animate_product(self, left, right, result): + l_matrix = left.get_mob_matrix() + r_matrix = right.get_mob_matrix() + result_matrix = result.get_mob_matrix() + circle = Circle( + radius = l_matrix[0][0].get_height(), + color = GREEN + ) + circles = VGroup(*[ + entry.get_point_mobject() + for entry in l_matrix[0][0], r_matrix[0][0] + ]) + (m, k), n = l_matrix.shape, r_matrix.shape[1] + for mob in result_matrix.flatten(): + mob.set_color(BLACK) + lagging_anims = [] + for a in range(m): + for b in range(n): + for c in range(k): + l_matrix[a][c].set_color(YELLOW) + r_matrix[c][b].set_color(YELLOW) + for c in range(k): + start_parts = VGroup( + l_matrix[a][c].copy(), + r_matrix[c][b].copy() + ) + result_entry = result_matrix[a][b].split()[c] + + new_circles = VGroup(*[ + circle.copy().shift(part.get_center()) + for part in start_parts.split() + ]) + self.play(Transform(circles, new_circles)) + self.play( + Transform( + start_parts, + result_entry.copy().set_color(YELLOW), + path_arc = -np.pi/2, + submobject_mode = "all_at_once", + ), + *lagging_anims + ) + result_entry.set_color(YELLOW) + self.remove(start_parts) + lagging_anims = [ + ApplyMethod(result_entry.set_color, WHITE) + ] + + for c in range(k): + l_matrix[a][c].set_color(WHITE) + r_matrix[c][b].set_color(WHITE) + self.play(FadeOut(circles), *lagging_anims) + self.wait() \ No newline at end of file diff --git a/scene/graph_scene.py b/scene/graph_scene.py index e7fe94f8..41b77b55 100644 --- a/scene/graph_scene.py +++ b/scene/graph_scene.py @@ -19,6 +19,9 @@ from utils.color import color_gradient from utils.color import invert_color from utils.space_ops import angle_of_vector +# TODO, this should probably reimplemented entirely, especially so as to +# better reuse code from mobject/coordinate_systems + class GraphScene(Scene): CONFIG = { "x_min" : -1, diff --git a/topics/matrix.py b/topics/matrix.py deleted file mode 100644 index 3aa60a0c..00000000 --- a/topics/matrix.py +++ /dev/null @@ -1,277 +0,0 @@ -import numpy as np - -from animation.creation import ShowCreation -from animation.creation import Write -from animation.transform import ApplyFunction -from animation.transform import ApplyMethod -from animation.transform import ApplyPointwiseFunction -from animation.creation import FadeOut -from animation.transform import Transform -from mobject.mobject import Mobject -from mobject.svg.tex_mobject import TexMobject -from mobject.svg.tex_mobject import TextMobject -from mobject.types.vectorized_mobject import VGroup -from mobject.types.vectorized_mobject import VMobject -from scene.scene import Scene -from mobject.geometry import Arrow -from mobject.shape_matchers import BackgroundRectangle -from mobject.geometry import Circle -from mobject.geometry import Dot -from mobject.geometry import Line -from mobject.geometry import Vector -from mobject.coordinate_systems import Axes -from mobject.coordinate_systems import NumberPlane - -from constants import * - -VECTOR_LABEL_SCALE_FACTOR = 0.8 - -def matrix_to_tex_string(matrix): - matrix = np.array(matrix).astype("string") - if matrix.ndim == 1: - matrix = matrix.reshape((matrix.size, 1)) - n_rows, n_cols = matrix.shape - prefix = "\\left[ \\begin{array}{%s}"%("c"*n_cols) - suffix = "\\end{array} \\right]" - rows = [ - " & ".join(row) - for row in matrix - ] - return prefix + " \\\\ ".join(rows) + suffix - -def matrix_to_mobject(matrix): - return TexMobject(matrix_to_tex_string(matrix)) - -def vector_coordinate_label(vector_mob, integer_labels = True, - n_dim = 2, color = WHITE): - vect = np.array(vector_mob.get_end()) - if integer_labels: - vect = np.round(vect).astype(int) - vect = vect[:n_dim] - vect = vect.reshape((n_dim, 1)) - label = Matrix(vect, add_background_rectangles = True) - label.scale(VECTOR_LABEL_SCALE_FACTOR) - - shift_dir = np.array(vector_mob.get_end()) - if shift_dir[0] >= 0: #Pointing right - shift_dir -= label.get_left() + DEFAULT_MOBJECT_TO_MOBJECT_BUFFER*LEFT - else: #Pointing left - shift_dir -= label.get_right() + DEFAULT_MOBJECT_TO_MOBJECT_BUFFER*RIGHT - label.shift(shift_dir) - label.set_color(color) - label.rect = BackgroundRectangle(label) - label.add_to_back(label.rect) - return label - -class Matrix(VMobject): - CONFIG = { - "v_buff" : 0.5, - "h_buff" : 1, - "add_background_rectangles" : False - } - def __init__(self, matrix, **kwargs): - """ - Matrix can either either include numbres, tex_strings, - or mobjects - """ - VMobject.__init__(self, **kwargs) - matrix = np.array(matrix) - if matrix.ndim == 1: - matrix = matrix.reshape((matrix.size, 1)) - if not isinstance(matrix[0][0], Mobject): - matrix = matrix.astype("string") - matrix = self.string_matrix_to_mob_matrix(matrix) - self.organize_mob_matrix(matrix) - self.add(*matrix.flatten()) - self.add_brackets() - self.center() - self.mob_matrix = matrix - if self.add_background_rectangles: - for mob in matrix.flatten(): - mob.add_background_rectangle() - - def string_matrix_to_mob_matrix(self, matrix): - return np.array([ - map(TexMobject, row) - for row in matrix - ]).reshape(matrix.shape) - - def organize_mob_matrix(self, matrix): - for i, row in enumerate(matrix): - for j, elem in enumerate(row): - mob = matrix[i][j] - if i == 0 and j == 0: - continue - elif i == 0: - mob.next_to(matrix[i][j-1], RIGHT, self.h_buff) - else: - mob.next_to(matrix[i-1][j], DOWN, self.v_buff) - return self - - def add_brackets(self): - bracket_pair = TexMobject("\\big[ \\big]") - bracket_pair.scale(2) - bracket_pair.stretch_to_fit_height(self.get_height() + 0.5) - l_bracket, r_bracket = bracket_pair.split() - l_bracket.next_to(self, LEFT) - r_bracket.next_to(self, RIGHT) - self.add(l_bracket, r_bracket) - self.brackets = VMobject(l_bracket, r_bracket) - return self - - def set_color_columns(self, *colors): - for i, color in enumerate(colors): - VMobject(*self.mob_matrix[:,i]).set_color(color) - return self - - def add_background_to_entries(self): - for mob in self.get_entries(): - mob.add_background_rectangle() - return self - - def get_mob_matrix(self): - return self.mob_matrix - - def get_entries(self): - return VMobject(*self.get_mob_matrix().flatten()) - - def get_brackets(self): - return self.brackets - -class NumericalMatrixMultiplication(Scene): - CONFIG = { - "left_matrix" : [[1, 2], [3, 4]], - "right_matrix" : [[5, 6], [7, 8]], - "use_parens" : True, - } - def construct(self): - left_string_matrix, right_string_matrix = [ - np.array(matrix).astype("string") - for matrix in self.left_matrix, self.right_matrix - ] - if right_string_matrix.shape[0] != left_string_matrix.shape[1]: - raise Exception("Incompatible shapes for matrix multiplication") - - left = Matrix(left_string_matrix) - right = Matrix(right_string_matrix) - result = self.get_result_matrix( - left_string_matrix, right_string_matrix - ) - - self.organize_matrices(left, right, result) - self.animate_product(left, right, result) - - - def get_result_matrix(self, left, right): - (m, k), n = left.shape, right.shape[1] - mob_matrix = np.array([VMobject()]).repeat(m*n).reshape((m, n)) - for a in range(m): - for b in range(n): - template = "(%s)(%s)" if self.use_parens else "%s%s" - parts = [ - prefix + template%(left[a][c], right[c][b]) - for c in range(k) - for prefix in ["" if c == 0 else "+"] - ] - mob_matrix[a][b] = TexMobject(parts, next_to_buff = 0.1) - return Matrix(mob_matrix) - - def add_lines(self, left, right): - line_kwargs = { - "color" : BLUE, - "stroke_width" : 2, - } - left_rows = [ - VMobject(*row) for row in left.get_mob_matrix() - ] - h_lines = VMobject() - for row in left_rows[:-1]: - h_line = Line(row.get_left(), row.get_right(), **line_kwargs) - h_line.next_to(row, DOWN, buff = left.v_buff/2.) - h_lines.add(h_line) - - right_cols = [ - VMobject(*col) for col in np.transpose(right.get_mob_matrix()) - ] - v_lines = VMobject() - for col in right_cols[:-1]: - v_line = Line(col.get_top(), col.get_bottom(), **line_kwargs) - v_line.next_to(col, RIGHT, buff = right.h_buff/2.) - v_lines.add(v_line) - - self.play(ShowCreation(h_lines)) - self.play(ShowCreation(v_lines)) - self.wait() - self.show_frame() - - def organize_matrices(self, left, right, result): - equals = TexMobject("=") - everything = VMobject(left, right, equals, result) - everything.arrange_submobjects() - everything.scale_to_fit_width(FRAME_WIDTH-1) - self.add(everything) - - - def animate_product(self, left, right, result): - l_matrix = left.get_mob_matrix() - r_matrix = right.get_mob_matrix() - result_matrix = result.get_mob_matrix() - circle = Circle( - radius = l_matrix[0][0].get_height(), - color = GREEN - ) - circles = VMobject(*[ - entry.get_point_mobject() - for entry in l_matrix[0][0], r_matrix[0][0] - ]) - (m, k), n = l_matrix.shape, r_matrix.shape[1] - for mob in result_matrix.flatten(): - mob.set_color(BLACK) - lagging_anims = [] - for a in range(m): - for b in range(n): - for c in range(k): - l_matrix[a][c].set_color(YELLOW) - r_matrix[c][b].set_color(YELLOW) - for c in range(k): - start_parts = VMobject( - l_matrix[a][c].copy(), - r_matrix[c][b].copy() - ) - result_entry = result_matrix[a][b].split()[c] - - new_circles = VMobject(*[ - circle.copy().shift(part.get_center()) - for part in start_parts.split() - ]) - self.play(Transform(circles, new_circles)) - self.play( - Transform( - start_parts, - result_entry.copy().set_color(YELLOW), - path_arc = -np.pi/2, - submobject_mode = "all_at_once", - ), - *lagging_anims - ) - result_entry.set_color(YELLOW) - self.remove(start_parts) - lagging_anims = [ - ApplyMethod(result_entry.set_color, WHITE) - ] - - for c in range(k): - l_matrix[a][c].set_color(WHITE) - r_matrix[c][b].set_color(WHITE) - self.play(FadeOut(circles), *lagging_anims) - self.wait() - - - - - - - - - - diff --git a/topics/vector_space_scene.py b/topics/vector_space_scene.py index e22cd90d..2e3c93f4 100644 --- a/topics/vector_space_scene.py +++ b/topics/vector_space_scene.py @@ -25,9 +25,9 @@ from mobject.coordinate_systems import Axes from mobject.coordinate_systems import NumberPlane from constants import * -from topics.matrix import Matrix -from topics.matrix import VECTOR_LABEL_SCALE_FACTOR -from topics.matrix import vector_coordinate_label +from mobject.matrix import Matrix +from mobject.matrix import VECTOR_LABEL_SCALE_FACTOR +from mobject.matrix import vector_coordinate_label from utils.rate_functions import rush_from from utils.rate_functions import rush_into from utils.space_ops import angle_of_vector From 10bde4ab7cafb38975d453bb2f699806f0417e7e Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Sun, 1 Apr 2018 10:22:52 -0700 Subject: [PATCH 15/17] Added missing colors to utils.color.py --- utils/color.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/utils/color.py b/utils/color.py index 3933feac..77e4a62a 100644 --- a/utils/color.py +++ b/utils/color.py @@ -2,6 +2,8 @@ import numpy as np import random from colour import Color +from colour import WHITE +from colour import PALETTE from utils.bezier import interpolate From ca7930740bcac1b3d580e6b59ee43763e5e5433e Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Sun, 1 Apr 2018 10:45:41 -0700 Subject: [PATCH 16/17] Factored probability and three_d constructs out of topics/ --- big_ol_pile_of_manim_imports.py | 10 +- .../three_d_camera.py | 138 +--- mobject/probability.py | 249 +++++++ mobject/svg/drawings.py | 278 +++++++- mobject/three_dimensions.py | 73 ++ once_useful_constructs/light.py | 4 +- scene/sample_space_scene.py | 146 ++++ scene/three_d_scene.py | 68 ++ {topics => scene}/vector_space_scene.py | 8 +- topics/__init__.py | 0 topics/probability.py | 652 ------------------ 11 files changed, 826 insertions(+), 800 deletions(-) rename topics/three_dimensions.py => camera/three_d_camera.py (61%) create mode 100644 mobject/probability.py create mode 100644 mobject/three_dimensions.py create mode 100644 scene/sample_space_scene.py create mode 100644 scene/three_d_scene.py rename {topics => scene}/vector_space_scene.py (99%) delete mode 100644 topics/__init__.py delete mode 100644 topics/probability.py diff --git a/big_ol_pile_of_manim_imports.py b/big_ol_pile_of_manim_imports.py index 72f7711b..58ead133 100644 --- a/big_ol_pile_of_manim_imports.py +++ b/big_ol_pile_of_manim_imports.py @@ -29,6 +29,7 @@ from animation.update import * from camera.camera import * from camera.mapping_camera import * from camera.moving_camera import * +from camera.three_d_camera import * from continual_animation.continual_animation import * from continual_animation.from_animation import * @@ -38,13 +39,16 @@ from continual_animation.update import * from mobject.frame import * from mobject.functions import * from mobject.geometry import * +from mobject.matrix import * from mobject.mobject import * from mobject.number_line import * from mobject.numbers import * +from mobject.probability import * from mobject.shape_matchers import * from mobject.svg.brace import * from mobject.svg.svg_mobject import * from mobject.svg.tex_mobject import * +from mobject.three_dimensions import * from mobject.types.image_mobject import * from mobject.types.point_cloud_mobject import * from mobject.types.vectorized_mobject import * @@ -60,6 +64,8 @@ from scene.moving_camera_scene import * from scene.reconfigurable_scene import * from scene.scene import * from scene.scene_from_video import * +from scene.three_d_scene import * +from scene.vector_space_scene import * from scene.zoomed_scene import * from once_useful_constructs.arithmetic import * @@ -70,10 +76,6 @@ from once_useful_constructs.fractals import * from once_useful_constructs.graph_theory import * from once_useful_constructs.light import * -from mobject.matrix import * -from topics.probability import * -from topics.three_dimensions import * -from topics.vector_space_scene import * from utils.bezier import * from utils.color import * diff --git a/topics/three_dimensions.py b/camera/three_d_camera.py similarity index 61% rename from topics/three_dimensions.py rename to camera/three_d_camera.py index 0718f4ed..a60fc953 100644 --- a/topics/three_dimensions.py +++ b/camera/three_d_camera.py @@ -1,22 +1,14 @@ +import numpy as np from constants import * -from continual_animation.continual_animation import ContinualMovement -from animation.transform import ApplyMethod from camera.camera import Camera -from mobject.types.vectorized_mobject import VGroup -from mobject.types.vectorized_mobject import VMobject from mobject.types.vectorized_mobject import VectorizedPoint -from scene.scene import Scene -from mobject.geometry import Line -from mobject.geometry import Square +from mobject.three_dimensions import should_shade_in_3d from utils.bezier import interpolate -from utils.iterables import list_update from utils.space_ops import rotation_about_z from utils.space_ops import rotation_matrix -from utils.space_ops import z_to_vector - class CameraWithPerspective(Camera): CONFIG = { @@ -47,6 +39,7 @@ class ThreeDCamera(CameraWithPerspective): Camera.__init__(self, *args, **kwargs) self.unit_sun_vect = self.sun_vect/np.linalg.norm(self.sun_vect) ## rotation_mobject lives in the phi-theta-distance space + ## TODO, use ValueTracker for this instead self.rotation_mobject = VectorizedPoint() ## moving_center lives in the x-y-z space ## It representes the center of rotation @@ -172,128 +165,3 @@ class ThreeDCamera(CameraWithPerspective): self.space_center = self.moving_center.points[0] return Camera.points_to_pixel_coords(self, new_points) - -class ThreeDScene(Scene): - CONFIG = { - "camera_class" : ThreeDCamera, - "ambient_camera_rotation" : None, - } - - def set_camera_position(self, phi = None, theta = None, distance = None, - center_x = None, center_y = None, center_z = None): - self.camera.set_position(phi, theta, distance, center_x, center_y, center_z) - - def begin_ambient_camera_rotation(self, rate = 0.01): - self.ambient_camera_rotation = ContinualMovement( - self.camera.rotation_mobject, - direction = UP, - rate = rate - ) - self.add(self.ambient_camera_rotation) - - def stop_ambient_camera_rotation(self): - if self.ambient_camera_rotation is not None: - self.remove(self.ambient_camera_rotation) - self.ambient_camera_rotation = None - - def move_camera( - self, - phi = None, theta = None, distance = None, - center_x = None, center_y = None, center_z = None, - added_anims = [], - **kwargs - ): - target_point = self.camera.get_spherical_coords(phi, theta, distance) - movement = ApplyMethod( - self.camera.rotation_mobject.move_to, - target_point, - **kwargs - ) - target_center = self.camera.get_center_of_rotation(center_x, center_y, center_z) - movement_center = ApplyMethod( - self.camera.moving_center.move_to, - target_center, - **kwargs - ) - is_camera_rotating = self.ambient_camera_rotation in self.continual_animations - if is_camera_rotating: - self.remove(self.ambient_camera_rotation) - self.play(movement, movement_center, *added_anims) - target_point = self.camera.get_spherical_coords(phi, theta, distance) - if is_camera_rotating: - self.add(self.ambient_camera_rotation) - - def get_moving_mobjects(self, *animations): - moving_mobjects = Scene.get_moving_mobjects(self, *animations) - if self.camera.rotation_mobject in moving_mobjects: - return list_update(self.mobjects, moving_mobjects) - return moving_mobjects - -############## - -def should_shade_in_3d(mobject): - return hasattr(mobject, "shade_in_3d") and mobject.shade_in_3d - -def shade_in_3d(mobject): - for submob in mobject.submobject_family(): - submob.shade_in_3d = True - -def turn_off_3d_shading(mobject): - for submob in mobject.submobject_family(): - submob.shade_in_3d = False - -class ThreeDMobject(VMobject): - def __init__(self, *args, **kwargs): - VMobject.__init__(self, *args, **kwargs) - shade_in_3d(self) - -class Cube(ThreeDMobject): - CONFIG = { - "fill_opacity" : 0.75, - "fill_color" : BLUE, - "stroke_width" : 0, - "propagate_style_to_family" : True, - "side_length" : 2, - } - def generate_points(self): - for vect in IN, OUT, LEFT, RIGHT, UP, DOWN: - face = Square(side_length = self.side_length) - face.shift(self.side_length*OUT/2.0) - face.apply_function(lambda p : np.dot(p, z_to_vector(vect).T)) - - self.add(face) - -class Prism(Cube): - CONFIG = { - "dimensions" : [3, 2, 1] - } - def generate_points(self): - Cube.generate_points(self) - for dim, value in enumerate(self.dimensions): - self.rescale_to_fit(value, dim, stretch = True) - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/mobject/probability.py b/mobject/probability.py new file mode 100644 index 00000000..5be9f1de --- /dev/null +++ b/mobject/probability.py @@ -0,0 +1,249 @@ +from constants import * + +from mobject.mobject import Mobject +from mobject.svg.brace import Brace +from mobject.svg.tex_mobject import TexMobject +from mobject.svg.tex_mobject import TextMobject +from mobject.types.vectorized_mobject import VGroup +from mobject.geometry import Line +from mobject.geometry import Rectangle + +from utils.color import color_gradient +from utils.iterables import tuplify + +EPSILON = 0.0001 + +class SampleSpace(Rectangle): + CONFIG = { + "height" : 3, + "width" : 3, + "fill_color" : DARK_GREY, + "fill_opacity" : 1, + "stroke_width" : 0.5, + "stroke_color" : LIGHT_GREY, + ## + "default_label_scale_val" : 1, + } + def add_title(self, title = "Sample space", buff = MED_SMALL_BUFF): + ##TODO, should this really exist in SampleSpaceScene + title_mob = TextMobject(title) + if title_mob.get_width() > self.get_width(): + title_mob.scale_to_fit_width(self.get_width()) + title_mob.next_to(self, UP, buff = buff) + self.title = title_mob + self.add(title_mob) + + def add_label(self, label): + self.label = label + + def complete_p_list(self, p_list): + new_p_list = list(tuplify(p_list)) + remainder = 1.0 - sum(new_p_list) + if abs(remainder) > EPSILON: + new_p_list.append(remainder) + return new_p_list + + def get_division_along_dimension(self, p_list, dim, colors, vect): + p_list = self.complete_p_list(p_list) + colors = color_gradient(colors, len(p_list)) + + last_point = self.get_edge_center(-vect) + parts = VGroup() + for factor, color in zip(p_list, colors): + part = SampleSpace() + part.set_fill(color, 1) + part.replace(self, stretch = True) + part.stretch(factor, dim) + part.move_to(last_point, -vect) + last_point = part.get_edge_center(vect) + parts.add(part) + return parts + + def get_horizontal_division( + self, p_list, + colors = [GREEN_E, BLUE_E], + vect = DOWN + ): + return self.get_division_along_dimension(p_list, 1, colors, vect) + + def get_vertical_division( + self, p_list, + colors = [MAROON_B, YELLOW], + vect = RIGHT + ): + return self.get_division_along_dimension(p_list, 0, colors, vect) + + def divide_horizontally(self, *args, **kwargs): + self.horizontal_parts = self.get_horizontal_division(*args, **kwargs) + self.add(self.horizontal_parts) + + def divide_vertically(self, *args, **kwargs): + self.vertical_parts = self.get_vertical_division(*args, **kwargs) + self.add(self.vertical_parts) + + def get_subdivision_braces_and_labels( + self, parts, labels, direction, + buff = SMALL_BUFF, + min_num_quads = 1 + ): + label_mobs = VGroup() + braces = VGroup() + for label, part in zip(labels, parts): + brace = Brace( + part, direction, + min_num_quads = min_num_quads, + buff = buff + ) + if isinstance(label, Mobject): + label_mob = label + else: + label_mob = TexMobject(label) + label_mob.scale(self.default_label_scale_val) + label_mob.next_to(brace, direction, buff) + + braces.add(brace) + label_mobs.add(label_mob) + parts.braces = braces + parts.labels = label_mobs + parts.label_kwargs = { + "labels" : label_mobs.copy(), + "direction" : direction, + "buff" : buff, + } + return VGroup(parts.braces, parts.labels) + + def get_side_braces_and_labels(self, labels, direction = LEFT, **kwargs): + assert(hasattr(self, "horizontal_parts")) + parts = self.horizontal_parts + return self.get_subdivision_braces_and_labels(parts, labels, direction, **kwargs) + + def get_top_braces_and_labels(self, labels, **kwargs): + assert(hasattr(self, "vertical_parts")) + parts = self.vertical_parts + return self.get_subdivision_braces_and_labels(parts, labels, UP, **kwargs) + + def get_bottom_braces_and_labels(self, labels, **kwargs): + assert(hasattr(self, "vertical_parts")) + parts = self.vertical_parts + return self.get_subdivision_braces_and_labels(parts, labels, DOWN, **kwargs) + + def add_braces_and_labels(self): + for attr in "horizontal_parts", "vertical_parts": + if not hasattr(self, attr): + continue + parts = getattr(self, attr) + for subattr in "braces", "labels": + if hasattr(parts, subattr): + self.add(getattr(parts, subattr)) + + def __getitem__(self, index): + if hasattr(self, "horizontal_parts"): + return self.horizontal_parts[index] + elif hasattr(self, "vertical_parts"): + return self.vertical_parts[index] + return self.split()[index] + +class BarChart(VGroup): + CONFIG = { + "height" : 4, + "width" : 6, + "n_ticks" : 4, + "tick_width" : 0.2, + "label_y_axis" : True, + "y_axis_label_height" : 0.25, + "max_value" : 1, + "bar_colors" : [BLUE, YELLOW], + "bar_fill_opacity" : 0.8, + "bar_stroke_width" : 3, + "bar_names" : [], + "bar_label_scale_val" : 0.75, + } + def __init__(self, values, **kwargs): + VGroup.__init__(self, **kwargs) + if self.max_value is None: + self.max_value = max(values) + + self.add_axes() + self.add_bars(values) + self.center() + + def add_axes(self): + x_axis = Line(self.tick_width*LEFT/2, self.width*RIGHT) + y_axis = Line(MED_LARGE_BUFF*DOWN, self.height*UP) + ticks = VGroup() + heights = np.linspace(0, self.height, self.n_ticks+1) + values = np.linspace(0, self.max_value, self.n_ticks+1) + for y, value in zip(heights, values): + tick = Line(LEFT, RIGHT) + tick.scale_to_fit_width(self.tick_width) + tick.move_to(y*UP) + ticks.add(tick) + y_axis.add(ticks) + + self.add(x_axis, y_axis) + self.x_axis, self.y_axis = x_axis, y_axis + + if self.label_y_axis: + labels = VGroup() + for tick, value in zip(ticks, values): + label = TexMobject(str(np.round(value, 2))) + label.scale_to_fit_height(self.y_axis_label_height) + label.next_to(tick, LEFT, SMALL_BUFF) + labels.add(label) + self.y_axis_labels = labels + self.add(labels) + + + def add_bars(self, values): + buff = float(self.width) / (2*len(values) + 1) + bars = VGroup() + for i, value in enumerate(values): + bar = Rectangle( + height = (value/self.max_value)*self.height, + width = buff, + stroke_width = self.bar_stroke_width, + fill_opacity = self.bar_fill_opacity, + ) + bar.move_to((2*i+1)*buff*RIGHT, DOWN+LEFT) + bars.add(bar) + bars.set_color_by_gradient(*self.bar_colors) + + bar_labels = VGroup() + for bar, name in zip(bars, self.bar_names): + label = TexMobject(str(name)) + label.scale(self.bar_label_scale_val) + label.next_to(bar, DOWN, SMALL_BUFF) + bar_labels.add(label) + + self.add(bars, bar_labels) + self.bars = bars + self.bar_labels = bar_labels + + def change_bar_values(self, values): + for bar, value in zip(self.bars, values): + bar_bottom = bar.get_bottom() + bar.stretch_to_fit_height( + (value/self.max_value)*self.height + ) + bar.move_to(bar_bottom, DOWN) + + def copy(self): + return self.deepcopy() + + + + + + + + + + + + + + + + + + diff --git a/mobject/svg/drawings.py b/mobject/svg/drawings.py index 9a76b2d6..9b4ac6ca 100644 --- a/mobject/svg/drawings.py +++ b/mobject/svg/drawings.py @@ -26,7 +26,7 @@ from mobject.geometry import Polygon from mobject.geometry import Rectangle from mobject.geometry import Square from mobject.shape_matchers import SurroundingRectangle -from topics.three_dimensions import Cube +from mobject.three_dimensions import Cube from utils.config_ops import digest_config from utils.config_ops import digest_locals from utils.space_ops import R3_to_complex @@ -584,6 +584,282 @@ class Car(SVGMobject): def get_rear_light(self): return self[1][8] + +### Cards ### + +class DeckOfCards(VGroup): + def __init__(self, **kwargs): + possible_values = map(str, range(1, 11)) + ["J", "Q", "K"] + possible_suits = ["hearts", "diamonds", "spades", "clubs"] + VGroup.__init__(self, *[ + PlayingCard(value = value, suit = suit, **kwargs) + for value in possible_values + for suit in possible_suits + ]) + +class PlayingCard(VGroup): + CONFIG = { + "value" : None, + "suit" : None, + "key" : None, ##String like "8H" or "KS" + "height" : 2, + "height_to_width" : 3.5/2.5, + "card_height_to_symbol_height" : 7, + "card_width_to_corner_num_width" : 10, + "card_height_to_corner_num_height" : 10, + "color" : LIGHT_GREY, + "turned_over" : False, + "possible_suits" : ["hearts", "diamonds", "spades", "clubs"], + "possible_values" : map(str, range(2, 11)) + ["J", "Q", "K", "A"], + } + + def __init__(self, key = None, **kwargs): + VGroup.__init__(self, key = key, **kwargs) + + def generate_points(self): + self.add(Rectangle( + height = self.height, + width = self.height/self.height_to_width, + stroke_color = WHITE, + stroke_width = 2, + fill_color = self.color, + fill_opacity = 1, + )) + if self.turned_over: + self.set_fill(DARK_GREY) + self.set_stroke(LIGHT_GREY) + contents = VectorizedPoint(self.get_center()) + else: + value = self.get_value() + symbol = self.get_symbol() + design = self.get_design(value, symbol) + corner_numbers = self.get_corner_numbers(value, symbol) + contents = VGroup(design, corner_numbers) + self.design = design + self.corner_numbers = corner_numbers + self.add(contents) + + def get_value(self): + value = self.value + if value is None: + if self.key is not None: + value = self.key[:-1] + else: + value = random.choice(self.possible_values) + value = string.upper(str(value)) + if value == "1": + value = "A" + if value not in self.possible_values: + raise Exception("Invalid card value") + + face_card_to_value = { + "J" : 11, + "Q" : 12, + "K" : 13, + "A" : 14, + } + try: + self.numerical_value = int(value) + except: + self.numerical_value = face_card_to_value[value] + return value + + def get_symbol(self): + suit = self.suit + if suit is None: + if self.key is not None: + suit = dict([ + (string.upper(s[0]), s) + for s in self.possible_suits + ])[string.upper(self.key[-1])] + else: + suit = random.choice(self.possible_suits) + if suit not in self.possible_suits: + raise Exception("Invalud suit value") + self.suit = suit + symbol_height = float(self.height) / self.card_height_to_symbol_height + symbol = SuitSymbol(suit, height = symbol_height) + return symbol + + def get_design(self, value, symbol): + if value == "A": + return self.get_ace_design(symbol) + if value in map(str, range(2, 11)): + return self.get_number_design(value, symbol) + else: + return self.get_face_card_design(value, symbol) + + def get_ace_design(self, symbol): + design = symbol.copy().scale(1.5) + design.move_to(self) + return design + + def get_number_design(self, value, symbol): + num = int(value) + n_rows = { + 2 : 2, + 3 : 3, + 4 : 2, + 5 : 2, + 6 : 3, + 7 : 3, + 8 : 3, + 9 : 4, + 10 : 4, + }[num] + n_cols = 1 if num in [2, 3] else 2 + insertion_indices = { + 5 : [0], + 7 : [0], + 8 : [0, 1], + 9 : [1], + 10 : [0, 2], + }.get(num, []) + + top = self.get_top() + symbol.get_height()*DOWN + bottom = self.get_bottom() + symbol.get_height()*UP + column_points = [ + interpolate(top, bottom, alpha) + for alpha in np.linspace(0, 1, n_rows) + ] + + design = VGroup(*[ + symbol.copy().move_to(point) + for point in column_points + ]) + if n_cols == 2: + space = 0.2*self.get_width() + column_copy = design.copy().shift(space*RIGHT) + design.shift(space*LEFT) + design.add(*column_copy) + design.add(*[ + symbol.copy().move_to( + center_of_mass(column_points[i:i+2]) + ) + for i in insertion_indices + ]) + for symbol in design: + if symbol.get_center()[1] < self.get_center()[1]: + symbol.rotate_in_place(np.pi) + return design + + def get_face_card_design(self, value, symbol): + from for_3b1b_videos.pi_creature import PiCreature + sub_rect = Rectangle( + stroke_color = BLACK, + fill_opacity = 0, + height = 0.9*self.get_height(), + width = 0.6*self.get_width(), + ) + sub_rect.move_to(self) + + # pi_color = average_color(symbol.get_color(), GREY) + pi_color = symbol.get_color() + pi_mode = { + "J" : "plain", + "Q" : "thinking", + "K" : "hooray" + }[value] + pi_creature = PiCreature( + mode = pi_mode, + color = pi_color, + ) + pi_creature.scale_to_fit_width(0.8*sub_rect.get_width()) + if value in ["Q", "K"]: + prefix = "king" if value == "K" else "queen" + crown = SVGMobject(file_name = prefix + "_crown") + crown.set_stroke(width = 0) + crown.set_fill(YELLOW, 1) + crown.stretch_to_fit_width(0.5*sub_rect.get_width()) + crown.stretch_to_fit_height(0.17*sub_rect.get_height()) + crown.move_to(pi_creature.eyes.get_center(), DOWN) + pi_creature.add_to_back(crown) + to_top_buff = 0 + else: + to_top_buff = SMALL_BUFF*sub_rect.get_height() + pi_creature.next_to(sub_rect.get_top(), DOWN, to_top_buff) + # pi_creature.shift(0.05*sub_rect.get_width()*RIGHT) + + pi_copy = pi_creature.copy() + pi_copy.rotate(np.pi, about_point = sub_rect.get_center()) + + return VGroup(sub_rect, pi_creature, pi_copy) + + def get_corner_numbers(self, value, symbol): + value_mob = TextMobject(value) + width = self.get_width()/self.card_width_to_corner_num_width + height = self.get_height()/self.card_height_to_corner_num_height + value_mob.scale_to_fit_width(width) + value_mob.stretch_to_fit_height(height) + value_mob.next_to( + self.get_corner(UP+LEFT), DOWN+RIGHT, + buff = MED_LARGE_BUFF*width + ) + value_mob.set_color(symbol.get_color()) + corner_symbol = symbol.copy() + corner_symbol.scale_to_fit_width(width) + corner_symbol.next_to( + value_mob, DOWN, + buff = MED_SMALL_BUFF*width + ) + corner_group = VGroup(value_mob, corner_symbol) + opposite_corner_group = corner_group.copy() + opposite_corner_group.rotate( + np.pi, about_point = self.get_center() + ) + + return VGroup(corner_group, opposite_corner_group) + +class SuitSymbol(SVGMobject): + CONFIG = { + "height" : 0.5, + "fill_opacity" : 1, + "stroke_width" : 0, + "red" : "#D02028", + "black" : BLACK, + } + def __init__(self, suit_name, **kwargs): + digest_config(self, kwargs) + suits_to_colors = { + "hearts" : self.red, + "diamonds" : self.red, + "spades" : self.black, + "clubs" : self.black, + } + if suit_name not in suits_to_colors: + raise Exception("Invalid suit name") + SVGMobject.__init__(self, file_name = suit_name, **kwargs) + + color = suits_to_colors[suit_name] + self.set_stroke(width = 0) + self.set_fill(color, 1) + self.scale_to_fit_height(self.height) + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mobject/three_dimensions.py b/mobject/three_dimensions.py new file mode 100644 index 00000000..1aabbd62 --- /dev/null +++ b/mobject/three_dimensions.py @@ -0,0 +1,73 @@ +from constants import * + +from mobject.vectorized_mobject import VMobject +from topics.geometry import Square + +from utils.space_ops import z_to_vector + +############## + +def should_shade_in_3d(mobject): + return hasattr(mobject, "shade_in_3d") and mobject.shade_in_3d + +def shade_in_3d(mobject): + for submob in mobject.submobject_family(): + submob.shade_in_3d = True + +def turn_off_3d_shading(mobject): + for submob in mobject.submobject_family(): + submob.shade_in_3d = False + +class ThreeDMobject(VMobject): + def __init__(self, *args, **kwargs): + VMobject.__init__(self, *args, **kwargs) + shade_in_3d(self) + +class Cube(ThreeDMobject): + CONFIG = { + "fill_opacity" : 0.75, + "fill_color" : BLUE, + "stroke_width" : 0, + "propagate_style_to_family" : True, + "side_length" : 2, + } + def generate_points(self): + for vect in IN, OUT, LEFT, RIGHT, UP, DOWN: + face = Square(side_length = self.side_length) + face.shift(self.side_length*OUT/2.0) + face.apply_function(lambda p : np.dot(p, z_to_vector(vect).T)) + + self.add(face) + +class Prism(Cube): + CONFIG = { + "dimensions" : [3, 2, 1] + } + def generate_points(self): + Cube.generate_points(self) + for dim, value in enumerate(self.dimensions): + self.rescale_to_fit(value, dim, stretch = True) + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/once_useful_constructs/light.py b/once_useful_constructs/light.py index 76165cd5..41af1beb 100644 --- a/once_useful_constructs/light.py +++ b/once_useful_constructs/light.py @@ -20,8 +20,8 @@ from animation.creation import FadeOut from camera.camera import Camera from scene.scene import Scene -from topics.three_dimensions import ThreeDCamera -from topics.three_dimensions import ThreeDScene +from camera.three_d_camera import ThreeDCamera +from scene.three_d_scene import ThreeDScene from utils.space_ops import angle_between from utils.space_ops import angle_between_vectors diff --git a/scene/sample_space_scene.py b/scene/sample_space_scene.py new file mode 100644 index 00000000..53bc7cac --- /dev/null +++ b/scene/sample_space_scene.py @@ -0,0 +1,146 @@ +from constants import * + +from scene.scene import Scene + +from animation.animation import Animation +from animation.transform import MoveToTarget +from animation.transform import Transform +from animation.update import UpdateFromFunc + +from mobject.types.vectorized_mobject import VGroup +from mobject.probability import SampleSpace + + +class SampleSpaceScene(Scene): + def get_sample_space(self, **config): + self.sample_space = SampleSpace(**config) + return self.sample_space + + def add_sample_space(self, **config): + self.add(self.get_sample_space(**config)) + + def get_division_change_animations( + self, sample_space, parts, p_list, + dimension = 1, + new_label_kwargs = None, + **kwargs + ): + if new_label_kwargs is None: + new_label_kwargs = {} + anims = [] + p_list = sample_space.complete_p_list(p_list) + space_copy = sample_space.copy() + + vect = DOWN if dimension == 1 else RIGHT + parts.generate_target() + for part, p in zip(parts.target, p_list): + part.replace(space_copy, stretch = True) + part.stretch(p, dimension) + parts.target.arrange_submobjects(vect, buff = 0) + parts.target.move_to(space_copy) + anims.append(MoveToTarget(parts)) + if hasattr(parts, "labels") and parts.labels is not None: + label_kwargs = parts.label_kwargs + label_kwargs.update(new_label_kwargs) + new_braces, new_labels = sample_space.get_subdivision_braces_and_labels( + parts.target, **label_kwargs + ) + anims += [ + Transform(parts.braces, new_braces), + Transform(parts.labels, new_labels), + ] + return anims + + def get_horizontal_division_change_animations(self, p_list, **kwargs): + assert(hasattr(self.sample_space, "horizontal_parts")) + return self.get_division_change_animations( + self.sample_space, self.sample_space.horizontal_parts, p_list, + dimension = 1, + **kwargs + ) + + def get_vertical_division_change_animations(self, p_list, **kwargs): + assert(hasattr(self.sample_space, "vertical_parts")) + return self.get_division_change_animations( + self.sample_space, self.sample_space.vertical_parts, p_list, + dimension = 0, + **kwargs + ) + + def get_conditional_change_anims( + self, sub_sample_space_index, value, post_rects = None, + **kwargs + ): + parts = self.sample_space.horizontal_parts + sub_sample_space = parts[sub_sample_space_index] + anims = self.get_division_change_animations( + sub_sample_space, sub_sample_space.vertical_parts, value, + dimension = 0, + **kwargs + ) + if post_rects is not None: + anims += self.get_posterior_rectangle_change_anims(post_rects) + return anims + + def get_top_conditional_change_anims(self, *args, **kwargs): + return self.get_conditional_change_anims(0, *args, **kwargs) + + def get_bottom_conditional_change_anims(self, *args, **kwargs): + return self.get_conditional_change_anims(1, *args, **kwargs) + + def get_prior_rectangles(self): + return VGroup(*[ + self.sample_space.horizontal_parts[i].vertical_parts[0] + for i in range(2) + ]) + + def get_posterior_rectangles(self, buff = MED_LARGE_BUFF): + prior_rects = self.get_prior_rectangles() + areas = [ + rect.get_width()*rect.get_height() + for rect in prior_rects + ] + total_area = sum(areas) + total_height = prior_rects.get_height() + + post_rects = prior_rects.copy() + for rect, area in zip(post_rects, areas): + rect.stretch_to_fit_height(total_height * area/total_area) + rect.stretch_to_fit_width( + area/rect.get_height() + ) + post_rects.arrange_submobjects(DOWN, buff = 0) + post_rects.next_to( + self.sample_space, RIGHT, buff + ) + return post_rects + + def get_posterior_rectangle_braces_and_labels( + self, post_rects, labels, direction = RIGHT, **kwargs + ): + return self.sample_space.get_subdivision_braces_and_labels( + post_rects, labels, direction, **kwargs + ) + + def update_posterior_braces(self, post_rects): + braces = post_rects.braces + labels = post_rects.labels + for rect, brace, label in zip(post_rects, braces, labels): + brace.stretch_to_fit_height(rect.get_height()) + brace.next_to(rect, RIGHT, SMALL_BUFF) + label.next_to(brace, RIGHT, SMALL_BUFF) + + def get_posterior_rectangle_change_anims(self, post_rects): + def update_rects(rects): + new_rects = self.get_posterior_rectangles() + Transform(rects, new_rects).update(1) + if hasattr(rects, "braces"): + self.update_posterior_braces(rects) + return rects + + anims = [UpdateFromFunc(post_rects, update_rects)] + if hasattr(post_rects, "braces"): + anims += map(Animation, [ + post_rects.labels, post_rects.braces + ]) + return anims diff --git a/scene/three_d_scene.py b/scene/three_d_scene.py new file mode 100644 index 00000000..884bc521 --- /dev/null +++ b/scene/three_d_scene.py @@ -0,0 +1,68 @@ + +from constants import * + +from continual_animation.continual_animation import ContinualMovement +from animation.transform import ApplyMethod +from camera.three_d_camera import ThreeDCamera +from mobject.vectorized_mobject import VGroup +from mobject.three_dimensions import should_shade_in_3d +from scene.scene import Scene + +from utils.iterables import list_update + + +class ThreeDScene(Scene): + CONFIG = { + "camera_class" : ThreeDCamera, + "ambient_camera_rotation" : None, + } + + def set_camera_position(self, phi = None, theta = None, distance = None, + center_x = None, center_y = None, center_z = None): + self.camera.set_position(phi, theta, distance, center_x, center_y, center_z) + + def begin_ambient_camera_rotation(self, rate = 0.01): + self.ambient_camera_rotation = ContinualMovement( + self.camera.rotation_mobject, + direction = UP, + rate = rate + ) + self.add(self.ambient_camera_rotation) + + def stop_ambient_camera_rotation(self): + if self.ambient_camera_rotation is not None: + self.remove(self.ambient_camera_rotation) + self.ambient_camera_rotation = None + + def move_camera( + self, + phi = None, theta = None, distance = None, + center_x = None, center_y = None, center_z = None, + added_anims = [], + **kwargs + ): + target_point = self.camera.get_spherical_coords(phi, theta, distance) + movement = ApplyMethod( + self.camera.rotation_mobject.move_to, + target_point, + **kwargs + ) + target_center = self.camera.get_center_of_rotation(center_x, center_y, center_z) + movement_center = ApplyMethod( + self.camera.moving_center.move_to, + target_center, + **kwargs + ) + is_camera_rotating = self.ambient_camera_rotation in self.continual_animations + if is_camera_rotating: + self.remove(self.ambient_camera_rotation) + self.play(movement, movement_center, *added_anims) + target_point = self.camera.get_spherical_coords(phi, theta, distance) + if is_camera_rotating: + self.add(self.ambient_camera_rotation) + + def get_moving_mobjects(self, *animations): + moving_mobjects = Scene.get_moving_mobjects(self, *animations) + if self.camera.rotation_mobject in moving_mobjects: + return list_update(self.mobjects, moving_mobjects) + return moving_mobjects diff --git a/topics/vector_space_scene.py b/scene/vector_space_scene.py similarity index 99% rename from topics/vector_space_scene.py rename to scene/vector_space_scene.py index 2e3c93f4..a98080b6 100644 --- a/topics/vector_space_scene.py +++ b/scene/vector_space_scene.py @@ -1,10 +1,11 @@ import numpy as np +from constants import * + from animation.animation import Animation from animation.creation import ShowCreation from animation.creation import Write from animation.transform import ApplyFunction -from animation.transform import ApplyMethod from animation.transform import ApplyPointwiseFunction from animation.creation import FadeOut from animation.transform import Transform @@ -15,8 +16,6 @@ from mobject.types.vectorized_mobject import VGroup from mobject.types.vectorized_mobject import VMobject from scene.scene import Scene from mobject.geometry import Arrow -from mobject.shape_matchers import BackgroundRectangle -from mobject.geometry import Circle from mobject.geometry import Dot from mobject.geometry import Line from mobject.geometry import Square @@ -24,7 +23,6 @@ from mobject.geometry import Vector from mobject.coordinate_systems import Axes from mobject.coordinate_systems import NumberPlane -from constants import * from mobject.matrix import Matrix from mobject.matrix import VECTOR_LABEL_SCALE_FACTOR from mobject.matrix import vector_coordinate_label @@ -32,12 +30,10 @@ from utils.rate_functions import rush_from from utils.rate_functions import rush_into from utils.space_ops import angle_of_vector - X_COLOR = GREEN_C Y_COLOR = RED_C Z_COLOR = BLUE_D - class VectorScene(Scene): CONFIG = { "basis_vector_stroke_width" : 6 diff --git a/topics/__init__.py b/topics/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/topics/probability.py b/topics/probability.py deleted file mode 100644 index 90d3c49c..00000000 --- a/topics/probability.py +++ /dev/null @@ -1,652 +0,0 @@ -from constants import * - -from scene.scene import Scene - -from animation.animation import Animation -from animation.transform import MoveToTarget -from animation.transform import Transform -from animation.update import UpdateFromFunc - -from mobject.mobject import Mobject -from mobject.svg.svg_mobject import SVGMobject -from mobject.svg.brace import Brace -from mobject.svg.tex_mobject import TexMobject -from mobject.svg.tex_mobject import TextMobject -from mobject.types.vectorized_mobject import VGroup -from mobject.types.vectorized_mobject import VMobject -from mobject.types.vectorized_mobject import VectorizedPoint -from mobject.geometry import Arc -from mobject.geometry import Circle -from mobject.geometry import Line -from mobject.geometry import Polygon -from mobject.geometry import Rectangle -from mobject.geometry import Square - -from utils.bezier import interpolate -from utils.color import average_color -from utils.color import color_gradient -from utils.config_ops import digest_config -from utils.iterables import tuplify -from utils.space_ops import center_of_mass - -EPSILON = 0.0001 - -class SampleSpaceScene(Scene): - def get_sample_space(self, **config): - self.sample_space = SampleSpace(**config) - return self.sample_space - - def add_sample_space(self, **config): - self.add(self.get_sample_space(**config)) - - def get_division_change_animations( - self, sample_space, parts, p_list, - dimension = 1, - new_label_kwargs = None, - **kwargs - ): - if new_label_kwargs is None: - new_label_kwargs = {} - anims = [] - p_list = sample_space.complete_p_list(p_list) - space_copy = sample_space.copy() - - vect = DOWN if dimension == 1 else RIGHT - parts.generate_target() - for part, p in zip(parts.target, p_list): - part.replace(space_copy, stretch = True) - part.stretch(p, dimension) - parts.target.arrange_submobjects(vect, buff = 0) - parts.target.move_to(space_copy) - anims.append(MoveToTarget(parts)) - if hasattr(parts, "labels") and parts.labels is not None: - label_kwargs = parts.label_kwargs - label_kwargs.update(new_label_kwargs) - new_braces, new_labels = sample_space.get_subdivision_braces_and_labels( - parts.target, **label_kwargs - ) - anims += [ - Transform(parts.braces, new_braces), - Transform(parts.labels, new_labels), - ] - return anims - - def get_horizontal_division_change_animations(self, p_list, **kwargs): - assert(hasattr(self.sample_space, "horizontal_parts")) - return self.get_division_change_animations( - self.sample_space, self.sample_space.horizontal_parts, p_list, - dimension = 1, - **kwargs - ) - - def get_vertical_division_change_animations(self, p_list, **kwargs): - assert(hasattr(self.sample_space, "vertical_parts")) - return self.get_division_change_animations( - self.sample_space, self.sample_space.vertical_parts, p_list, - dimension = 0, - **kwargs - ) - - def get_conditional_change_anims( - self, sub_sample_space_index, value, post_rects = None, - **kwargs - ): - parts = self.sample_space.horizontal_parts - sub_sample_space = parts[sub_sample_space_index] - anims = self.get_division_change_animations( - sub_sample_space, sub_sample_space.vertical_parts, value, - dimension = 0, - **kwargs - ) - if post_rects is not None: - anims += self.get_posterior_rectangle_change_anims(post_rects) - return anims - - def get_top_conditional_change_anims(self, *args, **kwargs): - return self.get_conditional_change_anims(0, *args, **kwargs) - - def get_bottom_conditional_change_anims(self, *args, **kwargs): - return self.get_conditional_change_anims(1, *args, **kwargs) - - def get_prior_rectangles(self): - return VGroup(*[ - self.sample_space.horizontal_parts[i].vertical_parts[0] - for i in range(2) - ]) - - def get_posterior_rectangles(self, buff = MED_LARGE_BUFF): - prior_rects = self.get_prior_rectangles() - areas = [ - rect.get_width()*rect.get_height() - for rect in prior_rects - ] - total_area = sum(areas) - total_height = prior_rects.get_height() - - post_rects = prior_rects.copy() - for rect, area in zip(post_rects, areas): - rect.stretch_to_fit_height(total_height * area/total_area) - rect.stretch_to_fit_width( - area/rect.get_height() - ) - post_rects.arrange_submobjects(DOWN, buff = 0) - post_rects.next_to( - self.sample_space, RIGHT, buff - ) - return post_rects - - def get_posterior_rectangle_braces_and_labels( - self, post_rects, labels, direction = RIGHT, **kwargs - ): - return self.sample_space.get_subdivision_braces_and_labels( - post_rects, labels, direction, **kwargs - ) - - def update_posterior_braces(self, post_rects): - braces = post_rects.braces - labels = post_rects.labels - for rect, brace, label in zip(post_rects, braces, labels): - brace.stretch_to_fit_height(rect.get_height()) - brace.next_to(rect, RIGHT, SMALL_BUFF) - label.next_to(brace, RIGHT, SMALL_BUFF) - - def get_posterior_rectangle_change_anims(self, post_rects): - def update_rects(rects): - new_rects = self.get_posterior_rectangles() - Transform(rects, new_rects).update(1) - if hasattr(rects, "braces"): - self.update_posterior_braces(rects) - return rects - - anims = [UpdateFromFunc(post_rects, update_rects)] - if hasattr(post_rects, "braces"): - anims += map(Animation, [ - post_rects.labels, post_rects.braces - ]) - return anims - -class SampleSpace(Rectangle): - CONFIG = { - "height" : 3, - "width" : 3, - "fill_color" : DARK_GREY, - "fill_opacity" : 1, - "stroke_width" : 0.5, - "stroke_color" : LIGHT_GREY, - ## - "default_label_scale_val" : 1, - } - def add_title(self, title = "Sample space", buff = MED_SMALL_BUFF): - ##TODO, should this really exist in SampleSpaceScene - title_mob = TextMobject(title) - if title_mob.get_width() > self.get_width(): - title_mob.scale_to_fit_width(self.get_width()) - title_mob.next_to(self, UP, buff = buff) - self.title = title_mob - self.add(title_mob) - - def add_label(self, label): - self.label = label - - def complete_p_list(self, p_list): - new_p_list = list(tuplify(p_list)) - remainder = 1.0 - sum(new_p_list) - if abs(remainder) > EPSILON: - new_p_list.append(remainder) - return new_p_list - - def get_division_along_dimension(self, p_list, dim, colors, vect): - p_list = self.complete_p_list(p_list) - colors = color_gradient(colors, len(p_list)) - - last_point = self.get_edge_center(-vect) - parts = VGroup() - for factor, color in zip(p_list, colors): - part = SampleSpace() - part.set_fill(color, 1) - part.replace(self, stretch = True) - part.stretch(factor, dim) - part.move_to(last_point, -vect) - last_point = part.get_edge_center(vect) - parts.add(part) - return parts - - def get_horizontal_division( - self, p_list, - colors = [GREEN_E, BLUE_E], - vect = DOWN - ): - return self.get_division_along_dimension(p_list, 1, colors, vect) - - def get_vertical_division( - self, p_list, - colors = [MAROON_B, YELLOW], - vect = RIGHT - ): - return self.get_division_along_dimension(p_list, 0, colors, vect) - - def divide_horizontally(self, *args, **kwargs): - self.horizontal_parts = self.get_horizontal_division(*args, **kwargs) - self.add(self.horizontal_parts) - - def divide_vertically(self, *args, **kwargs): - self.vertical_parts = self.get_vertical_division(*args, **kwargs) - self.add(self.vertical_parts) - - def get_subdivision_braces_and_labels( - self, parts, labels, direction, - buff = SMALL_BUFF, - min_num_quads = 1 - ): - label_mobs = VGroup() - braces = VGroup() - for label, part in zip(labels, parts): - brace = Brace( - part, direction, - min_num_quads = min_num_quads, - buff = buff - ) - if isinstance(label, Mobject): - label_mob = label - else: - label_mob = TexMobject(label) - label_mob.scale(self.default_label_scale_val) - label_mob.next_to(brace, direction, buff) - - braces.add(brace) - label_mobs.add(label_mob) - parts.braces = braces - parts.labels = label_mobs - parts.label_kwargs = { - "labels" : label_mobs.copy(), - "direction" : direction, - "buff" : buff, - } - return VGroup(parts.braces, parts.labels) - - def get_side_braces_and_labels(self, labels, direction = LEFT, **kwargs): - assert(hasattr(self, "horizontal_parts")) - parts = self.horizontal_parts - return self.get_subdivision_braces_and_labels(parts, labels, direction, **kwargs) - - def get_top_braces_and_labels(self, labels, **kwargs): - assert(hasattr(self, "vertical_parts")) - parts = self.vertical_parts - return self.get_subdivision_braces_and_labels(parts, labels, UP, **kwargs) - - def get_bottom_braces_and_labels(self, labels, **kwargs): - assert(hasattr(self, "vertical_parts")) - parts = self.vertical_parts - return self.get_subdivision_braces_and_labels(parts, labels, DOWN, **kwargs) - - def add_braces_and_labels(self): - for attr in "horizontal_parts", "vertical_parts": - if not hasattr(self, attr): - continue - parts = getattr(self, attr) - for subattr in "braces", "labels": - if hasattr(parts, subattr): - self.add(getattr(parts, subattr)) - - def __getitem__(self, index): - if hasattr(self, "horizontal_parts"): - return self.horizontal_parts[index] - elif hasattr(self, "vertical_parts"): - return self.vertical_parts[index] - return self.split()[index] - -class BarChart(VGroup): - CONFIG = { - "height" : 4, - "width" : 6, - "n_ticks" : 4, - "tick_width" : 0.2, - "label_y_axis" : True, - "y_axis_label_height" : 0.25, - "max_value" : 1, - "bar_colors" : [BLUE, YELLOW], - "bar_fill_opacity" : 0.8, - "bar_stroke_width" : 3, - "bar_names" : [], - "bar_label_scale_val" : 0.75, - } - def __init__(self, values, **kwargs): - VGroup.__init__(self, **kwargs) - if self.max_value is None: - self.max_value = max(values) - - self.add_axes() - self.add_bars(values) - self.center() - - def add_axes(self): - x_axis = Line(self.tick_width*LEFT/2, self.width*RIGHT) - y_axis = Line(MED_LARGE_BUFF*DOWN, self.height*UP) - ticks = VGroup() - heights = np.linspace(0, self.height, self.n_ticks+1) - values = np.linspace(0, self.max_value, self.n_ticks+1) - for y, value in zip(heights, values): - tick = Line(LEFT, RIGHT) - tick.scale_to_fit_width(self.tick_width) - tick.move_to(y*UP) - ticks.add(tick) - y_axis.add(ticks) - - self.add(x_axis, y_axis) - self.x_axis, self.y_axis = x_axis, y_axis - - if self.label_y_axis: - labels = VGroup() - for tick, value in zip(ticks, values): - label = TexMobject(str(np.round(value, 2))) - label.scale_to_fit_height(self.y_axis_label_height) - label.next_to(tick, LEFT, SMALL_BUFF) - labels.add(label) - self.y_axis_labels = labels - self.add(labels) - - - def add_bars(self, values): - buff = float(self.width) / (2*len(values) + 1) - bars = VGroup() - for i, value in enumerate(values): - bar = Rectangle( - height = (value/self.max_value)*self.height, - width = buff, - stroke_width = self.bar_stroke_width, - fill_opacity = self.bar_fill_opacity, - ) - bar.move_to((2*i+1)*buff*RIGHT, DOWN+LEFT) - bars.add(bar) - bars.set_color_by_gradient(*self.bar_colors) - - bar_labels = VGroup() - for bar, name in zip(bars, self.bar_names): - label = TexMobject(str(name)) - label.scale(self.bar_label_scale_val) - label.next_to(bar, DOWN, SMALL_BUFF) - bar_labels.add(label) - - self.add(bars, bar_labels) - self.bars = bars - self.bar_labels = bar_labels - - def change_bar_values(self, values): - for bar, value in zip(self.bars, values): - bar_bottom = bar.get_bottom() - bar.stretch_to_fit_height( - (value/self.max_value)*self.height - ) - bar.move_to(bar_bottom, DOWN) - - def copy(self): - return self.deepcopy() - -### Cards ### - -class DeckOfCards(VGroup): - def __init__(self, **kwargs): - possible_values = map(str, range(1, 11)) + ["J", "Q", "K"] - possible_suits = ["hearts", "diamonds", "spades", "clubs"] - VGroup.__init__(self, *[ - PlayingCard(value = value, suit = suit, **kwargs) - for value in possible_values - for suit in possible_suits - ]) - -class PlayingCard(VGroup): - CONFIG = { - "value" : None, - "suit" : None, - "key" : None, ##String like "8H" or "KS" - "height" : 2, - "height_to_width" : 3.5/2.5, - "card_height_to_symbol_height" : 7, - "card_width_to_corner_num_width" : 10, - "card_height_to_corner_num_height" : 10, - "color" : LIGHT_GREY, - "turned_over" : False, - "possible_suits" : ["hearts", "diamonds", "spades", "clubs"], - "possible_values" : map(str, range(2, 11)) + ["J", "Q", "K", "A"], - } - - def __init__(self, key = None, **kwargs): - VGroup.__init__(self, key = key, **kwargs) - - def generate_points(self): - self.add(Rectangle( - height = self.height, - width = self.height/self.height_to_width, - stroke_color = WHITE, - stroke_width = 2, - fill_color = self.color, - fill_opacity = 1, - )) - if self.turned_over: - self.set_fill(DARK_GREY) - self.set_stroke(LIGHT_GREY) - contents = VectorizedPoint(self.get_center()) - else: - value = self.get_value() - symbol = self.get_symbol() - design = self.get_design(value, symbol) - corner_numbers = self.get_corner_numbers(value, symbol) - contents = VGroup(design, corner_numbers) - self.design = design - self.corner_numbers = corner_numbers - self.add(contents) - - def get_value(self): - value = self.value - if value is None: - if self.key is not None: - value = self.key[:-1] - else: - value = random.choice(self.possible_values) - value = string.upper(str(value)) - if value == "1": - value = "A" - if value not in self.possible_values: - raise Exception("Invalid card value") - - face_card_to_value = { - "J" : 11, - "Q" : 12, - "K" : 13, - "A" : 14, - } - try: - self.numerical_value = int(value) - except: - self.numerical_value = face_card_to_value[value] - return value - - def get_symbol(self): - suit = self.suit - if suit is None: - if self.key is not None: - suit = dict([ - (string.upper(s[0]), s) - for s in self.possible_suits - ])[string.upper(self.key[-1])] - else: - suit = random.choice(self.possible_suits) - if suit not in self.possible_suits: - raise Exception("Invalud suit value") - self.suit = suit - symbol_height = float(self.height) / self.card_height_to_symbol_height - symbol = SuitSymbol(suit, height = symbol_height) - return symbol - - def get_design(self, value, symbol): - if value == "A": - return self.get_ace_design(symbol) - if value in map(str, range(2, 11)): - return self.get_number_design(value, symbol) - else: - return self.get_face_card_design(value, symbol) - - def get_ace_design(self, symbol): - design = symbol.copy().scale(1.5) - design.move_to(self) - return design - - def get_number_design(self, value, symbol): - num = int(value) - n_rows = { - 2 : 2, - 3 : 3, - 4 : 2, - 5 : 2, - 6 : 3, - 7 : 3, - 8 : 3, - 9 : 4, - 10 : 4, - }[num] - n_cols = 1 if num in [2, 3] else 2 - insertion_indices = { - 5 : [0], - 7 : [0], - 8 : [0, 1], - 9 : [1], - 10 : [0, 2], - }.get(num, []) - - top = self.get_top() + symbol.get_height()*DOWN - bottom = self.get_bottom() + symbol.get_height()*UP - column_points = [ - interpolate(top, bottom, alpha) - for alpha in np.linspace(0, 1, n_rows) - ] - - design = VGroup(*[ - symbol.copy().move_to(point) - for point in column_points - ]) - if n_cols == 2: - space = 0.2*self.get_width() - column_copy = design.copy().shift(space*RIGHT) - design.shift(space*LEFT) - design.add(*column_copy) - design.add(*[ - symbol.copy().move_to( - center_of_mass(column_points[i:i+2]) - ) - for i in insertion_indices - ]) - for symbol in design: - if symbol.get_center()[1] < self.get_center()[1]: - symbol.rotate_in_place(np.pi) - return design - - def get_face_card_design(self, value, symbol): - from for_3b1b_videos.pi_creature import PiCreature - sub_rect = Rectangle( - stroke_color = BLACK, - fill_opacity = 0, - height = 0.9*self.get_height(), - width = 0.6*self.get_width(), - ) - sub_rect.move_to(self) - - # pi_color = average_color(symbol.get_color(), GREY) - pi_color = symbol.get_color() - pi_mode = { - "J" : "plain", - "Q" : "thinking", - "K" : "hooray" - }[value] - pi_creature = PiCreature( - mode = pi_mode, - color = pi_color, - ) - pi_creature.scale_to_fit_width(0.8*sub_rect.get_width()) - if value in ["Q", "K"]: - prefix = "king" if value == "K" else "queen" - crown = SVGMobject(file_name = prefix + "_crown") - crown.set_stroke(width = 0) - crown.set_fill(YELLOW, 1) - crown.stretch_to_fit_width(0.5*sub_rect.get_width()) - crown.stretch_to_fit_height(0.17*sub_rect.get_height()) - crown.move_to(pi_creature.eyes.get_center(), DOWN) - pi_creature.add_to_back(crown) - to_top_buff = 0 - else: - to_top_buff = SMALL_BUFF*sub_rect.get_height() - pi_creature.next_to(sub_rect.get_top(), DOWN, to_top_buff) - # pi_creature.shift(0.05*sub_rect.get_width()*RIGHT) - - pi_copy = pi_creature.copy() - pi_copy.rotate(np.pi, about_point = sub_rect.get_center()) - - return VGroup(sub_rect, pi_creature, pi_copy) - - def get_corner_numbers(self, value, symbol): - value_mob = TextMobject(value) - width = self.get_width()/self.card_width_to_corner_num_width - height = self.get_height()/self.card_height_to_corner_num_height - value_mob.scale_to_fit_width(width) - value_mob.stretch_to_fit_height(height) - value_mob.next_to( - self.get_corner(UP+LEFT), DOWN+RIGHT, - buff = MED_LARGE_BUFF*width - ) - value_mob.set_color(symbol.get_color()) - corner_symbol = symbol.copy() - corner_symbol.scale_to_fit_width(width) - corner_symbol.next_to( - value_mob, DOWN, - buff = MED_SMALL_BUFF*width - ) - corner_group = VGroup(value_mob, corner_symbol) - opposite_corner_group = corner_group.copy() - opposite_corner_group.rotate( - np.pi, about_point = self.get_center() - ) - - return VGroup(corner_group, opposite_corner_group) - -class SuitSymbol(SVGMobject): - CONFIG = { - "height" : 0.5, - "fill_opacity" : 1, - "stroke_width" : 0, - "red" : "#D02028", - "black" : BLACK, - } - def __init__(self, suit_name, **kwargs): - digest_config(self, kwargs) - suits_to_colors = { - "hearts" : self.red, - "diamonds" : self.red, - "spades" : self.black, - "clubs" : self.black, - } - if suit_name not in suits_to_colors: - raise Exception("Invalid suit name") - SVGMobject.__init__(self, file_name = suit_name, **kwargs) - - color = suits_to_colors[suit_name] - self.set_stroke(width = 0) - self.set_fill(color, 1) - self.scale_to_fit_height(self.height) - - - - - - - - - - - - - - - - - - - From 01ef5fc2c7de7e8bb4e03708b97054522f910684 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Sun, 1 Apr 2018 10:51:54 -0700 Subject: [PATCH 17/17] Several import fixes --- camera/three_d_camera.py | 2 ++ mobject/geometry.py | 7 ++++++- mobject/matrix.py | 2 ++ mobject/probability.py | 2 ++ mobject/three_dimensions.py | 6 ++++-- once_useful_constructs/light.py | 1 + scene/three_d_scene.py | 3 ++- scene/vector_space_scene.py | 2 ++ utils/color.py | 4 ++-- 9 files changed, 23 insertions(+), 6 deletions(-) diff --git a/camera/three_d_camera.py b/camera/three_d_camera.py index a60fc953..8054bef9 100644 --- a/camera/three_d_camera.py +++ b/camera/three_d_camera.py @@ -1,3 +1,5 @@ +from __future__ import absolute_import + import numpy as np from constants import * diff --git a/mobject/geometry.py b/mobject/geometry.py index a2182d5b..a39c0056 100644 --- a/mobject/geometry.py +++ b/mobject/geometry.py @@ -631,7 +631,12 @@ class Vector(Arrow): class DoubleArrow(Arrow): def init_tip(self): - self.tip = [(self.add_tip(), True), (self.add_tip(add_at_end = False), False)] + self.tip = VGroup() + for b in True, False: + t = self.add_tip(add_at_end = b) + t.add_at_end = b + self.tip.add(t) + self.tip.match_style(self.tip[0]) class CubicBezier(VMobject): def __init__(self, points, **kwargs): diff --git a/mobject/matrix.py b/mobject/matrix.py index 279a076c..cf20f6de 100644 --- a/mobject/matrix.py +++ b/mobject/matrix.py @@ -1,3 +1,5 @@ +from __future__ import absolute_import + import numpy as np from mobject.mobject import Mobject diff --git a/mobject/probability.py b/mobject/probability.py index 5be9f1de..d4a1ef93 100644 --- a/mobject/probability.py +++ b/mobject/probability.py @@ -1,3 +1,5 @@ +from __future__ import absolute_import + from constants import * from mobject.mobject import Mobject diff --git a/mobject/three_dimensions.py b/mobject/three_dimensions.py index 1aabbd62..29b13716 100644 --- a/mobject/three_dimensions.py +++ b/mobject/three_dimensions.py @@ -1,7 +1,9 @@ +from __future__ import absolute_import + from constants import * -from mobject.vectorized_mobject import VMobject -from topics.geometry import Square +from mobject.types.vectorized_mobject import VMobject +from mobject.geometry import Square from utils.space_ops import z_to_vector diff --git a/once_useful_constructs/light.py b/once_useful_constructs/light.py index 41af1beb..b5462630 100644 --- a/once_useful_constructs/light.py +++ b/once_useful_constructs/light.py @@ -2,6 +2,7 @@ from constants import * from mobject.geometry import AnnularSector from mobject.geometry import Arc +from mobject.geometry import Annulus from mobject.mobject import Mobject from mobject.svg.svg_mobject import SVGMobject from mobject.svg.tex_mobject import TexMobject diff --git a/scene/three_d_scene.py b/scene/three_d_scene.py index 884bc521..6896e55f 100644 --- a/scene/three_d_scene.py +++ b/scene/three_d_scene.py @@ -1,10 +1,11 @@ +from __future__ import absolute_import from constants import * from continual_animation.continual_animation import ContinualMovement from animation.transform import ApplyMethod from camera.three_d_camera import ThreeDCamera -from mobject.vectorized_mobject import VGroup +from mobject.types.vectorized_mobject import VGroup from mobject.three_dimensions import should_shade_in_3d from scene.scene import Scene diff --git a/scene/vector_space_scene.py b/scene/vector_space_scene.py index a98080b6..157561e1 100644 --- a/scene/vector_space_scene.py +++ b/scene/vector_space_scene.py @@ -1,3 +1,5 @@ +from __future__ import absolute_import + import numpy as np from constants import * diff --git a/utils/color.py b/utils/color.py index 77e4a62a..a4e11082 100644 --- a/utils/color.py +++ b/utils/color.py @@ -2,8 +2,8 @@ import numpy as np import random from colour import Color -from colour import WHITE -from colour import PALETTE +from constants import WHITE +from constants import PALETTE from utils.bezier import interpolate