mirror of
https://github.com/3b1b/manim.git
synced 2025-11-14 16:27:45 +00:00
Merge pull request #180 from 3b1b/simple-improvements
Factored helper functions into better organized utils folder
This commit is contained in:
commit
2945296083
77 changed files with 1044 additions and 959 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -12,6 +12,7 @@ ben_cairo_test.py
|
||||||
.flooignore
|
.flooignore
|
||||||
*.xml
|
*.xml
|
||||||
*.iml
|
*.iml
|
||||||
|
manim.sublime-project
|
||||||
|
manim.sublime-workspace
|
||||||
|
|
||||||
primes.py
|
primes.py
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
from helpers import *
|
from constants import *
|
||||||
|
|
||||||
from mobject.tex_mobject import TexMobject
|
from mobject.tex_mobject import TexMobject
|
||||||
from mobject import Mobject
|
from mobject.mobject import Mobject
|
||||||
from mobject.image_mobject import ImageMobject
|
from mobject.image_mobject import ImageMobject
|
||||||
from mobject.vectorized_mobject import *
|
from mobject.vectorized_mobject import *
|
||||||
|
|
||||||
|
|
@ -22,10 +22,10 @@ from topics.objects import *
|
||||||
from topics.complex_numbers import *
|
from topics.complex_numbers import *
|
||||||
from topics.common_scenes import *
|
from topics.common_scenes import *
|
||||||
from topics.probability import *
|
from topics.probability import *
|
||||||
from scene import Scene
|
from scene.scene import Scene
|
||||||
from scene.reconfigurable_scene import ReconfigurableScene
|
from scene.reconfigurable_scene import ReconfigurableScene
|
||||||
from scene.zoomed_scene import *
|
from scene.zoomed_scene import *
|
||||||
from camera import Camera
|
from camera.camera import Camera
|
||||||
from mobject.svg_mobject import *
|
from mobject.svg_mobject import *
|
||||||
from mobject.tex_mobject import *
|
from mobject.tex_mobject import *
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
from helpers import *
|
from constants import *
|
||||||
|
|
||||||
from mobject.tex_mobject import TexMobject
|
from mobject.tex_mobject import TexMobject
|
||||||
from mobject import Mobject
|
from mobject.mobject import Mobject
|
||||||
from mobject.image_mobject import ImageMobject
|
from mobject.image_mobject import ImageMobject
|
||||||
from mobject.vectorized_mobject import *
|
from mobject.vectorized_mobject import *
|
||||||
|
|
||||||
|
|
@ -22,10 +22,10 @@ from topics.objects import *
|
||||||
from topics.complex_numbers import *
|
from topics.complex_numbers import *
|
||||||
from topics.common_scenes import *
|
from topics.common_scenes import *
|
||||||
from topics.probability import *
|
from topics.probability import *
|
||||||
from scene import Scene
|
from scene.scene import Scene
|
||||||
from scene.reconfigurable_scene import ReconfigurableScene
|
from scene.reconfigurable_scene import ReconfigurableScene
|
||||||
from scene.zoomed_scene import *
|
from scene.zoomed_scene import *
|
||||||
from camera import Camera
|
from camera.camera import Camera
|
||||||
from mobject.svg_mobject import *
|
from mobject.svg_mobject import *
|
||||||
from mobject.tex_mobject import *
|
from mobject.tex_mobject import *
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
from helpers import *
|
from constants import *
|
||||||
|
|
||||||
from mobject.tex_mobject import TexMobject
|
from mobject.tex_mobject import TexMobject
|
||||||
from mobject import Mobject
|
from mobject.mobject import Mobject
|
||||||
from mobject.image_mobject import ImageMobject
|
from mobject.image_mobject import ImageMobject
|
||||||
from mobject.vectorized_mobject import *
|
from mobject.vectorized_mobject import *
|
||||||
|
|
||||||
|
|
@ -22,10 +22,10 @@ from topics.three_dimensions import *
|
||||||
from topics.objects import *
|
from topics.objects import *
|
||||||
from topics.probability import *
|
from topics.probability import *
|
||||||
from topics.complex_numbers import *
|
from topics.complex_numbers import *
|
||||||
from scene import Scene
|
from scene.scene import Scene
|
||||||
from scene.reconfigurable_scene import ReconfigurableScene
|
from scene.reconfigurable_scene import ReconfigurableScene
|
||||||
from scene.zoomed_scene import *
|
from scene.zoomed_scene import *
|
||||||
from camera import Camera
|
from camera.camera import Camera
|
||||||
from mobject.svg_mobject import *
|
from mobject.svg_mobject import *
|
||||||
from mobject.tex_mobject import *
|
from mobject.tex_mobject import *
|
||||||
from topics.graph_scene import *
|
from topics.graph_scene import *
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
from helpers import *
|
from constants import *
|
||||||
|
|
||||||
from mobject.tex_mobject import TexMobject
|
from mobject.tex_mobject import TexMobject
|
||||||
from mobject import Mobject
|
from mobject.mobject import Mobject
|
||||||
from mobject.image_mobject import ImageMobject
|
from mobject.image_mobject import ImageMobject
|
||||||
from mobject.vectorized_mobject import *
|
from mobject.vectorized_mobject import *
|
||||||
|
|
||||||
|
|
@ -22,10 +22,10 @@ from topics.objects import *
|
||||||
from topics.complex_numbers import *
|
from topics.complex_numbers import *
|
||||||
from topics.common_scenes import *
|
from topics.common_scenes import *
|
||||||
from topics.probability import *
|
from topics.probability import *
|
||||||
from scene import Scene
|
from scene.scene import Scene
|
||||||
from scene.reconfigurable_scene import ReconfigurableScene
|
from scene.reconfigurable_scene import ReconfigurableScene
|
||||||
from scene.zoomed_scene import *
|
from scene.zoomed_scene import *
|
||||||
from camera import Camera
|
from camera.camera import Camera
|
||||||
from mobject.svg_mobject import *
|
from mobject.svg_mobject import *
|
||||||
from mobject.tex_mobject import *
|
from mobject.tex_mobject import *
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,4 @@ __all__ = [
|
||||||
"animation",
|
"animation",
|
||||||
"simple_animations",
|
"simple_animations",
|
||||||
"transform"
|
"transform"
|
||||||
]
|
]
|
||||||
|
|
||||||
from animation import Animation
|
|
||||||
|
|
@ -1,15 +1,11 @@
|
||||||
from PIL import Image
|
|
||||||
from colour import Color
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import warnings
|
|
||||||
import time
|
|
||||||
import os
|
|
||||||
import progressbar
|
|
||||||
import inspect
|
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
|
|
||||||
from helpers import *
|
from constants import *
|
||||||
from mobject import Mobject
|
from mobject.mobject import Mobject
|
||||||
|
from utils.rate_functions import smooth
|
||||||
|
from utils.config_ops import instantiate
|
||||||
|
from utils.config_ops import digest_config
|
||||||
|
|
||||||
class Animation(object):
|
class Animation(object):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,17 @@
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import itertools as it
|
import itertools as it
|
||||||
|
|
||||||
from helpers import *
|
from constants import *
|
||||||
|
|
||||||
import warnings
|
import warnings
|
||||||
from mobject import Mobject, Group
|
from mobject.mobject import Mobject, Group
|
||||||
from mobject.vectorized_mobject import VMobject
|
from mobject.vectorized_mobject import VMobject
|
||||||
from mobject.tex_mobject import TextMobject
|
from mobject.tex_mobject import TextMobject
|
||||||
from animation import Animation
|
from .animation import Animation
|
||||||
from transform import Transform
|
from transform import Transform
|
||||||
|
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 LaggedStart(Animation):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
from helpers import *
|
from constants import *
|
||||||
from mobject import Mobject, Group
|
from mobject.mobject import Mobject, Group
|
||||||
from simple_animations import MaintainPositionRelativeTo
|
from simple_animations import MaintainPositionRelativeTo
|
||||||
import copy
|
import copy
|
||||||
|
from utils.config_ops import instantiate
|
||||||
|
from utils.config_ops import digest_config
|
||||||
|
|
||||||
class ContinualAnimation(object):
|
class ContinualAnimation(object):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,14 @@
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import operator as op
|
import operator as op
|
||||||
|
|
||||||
from animation import Animation
|
from .animation import Animation
|
||||||
from transform import Transform
|
from transform import Transform
|
||||||
from mobject import Mobject1D, Mobject
|
from mobject.mobject import Mobject
|
||||||
|
from mobject.point_cloud_mobject import Mobject1D
|
||||||
from topics.geometry import Line
|
from topics.geometry import Line
|
||||||
|
from utils.paths import path_along_arc
|
||||||
|
|
||||||
from helpers import *
|
from constants import *
|
||||||
|
|
||||||
class Vibrate(Animation):
|
class Vibrate(Animation):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,17 @@
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import itertools as it
|
import itertools as it
|
||||||
|
|
||||||
from helpers import *
|
from constants import *
|
||||||
|
|
||||||
import warnings
|
import warnings
|
||||||
from mobject import Mobject, Group
|
from mobject.mobject import Mobject, Group
|
||||||
from mobject.vectorized_mobject import VMobject
|
from mobject.vectorized_mobject import VMobject
|
||||||
from mobject.tex_mobject import TextMobject
|
from mobject.tex_mobject import TextMobject
|
||||||
from animation import Animation
|
from .animation import Animation
|
||||||
from transform import Transform
|
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):
|
class Rotating(Animation):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,17 @@ import inspect
|
||||||
import copy
|
import copy
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from helpers import *
|
from constants import *
|
||||||
|
|
||||||
from animation import Animation
|
from .animation import Animation
|
||||||
from mobject import Mobject, Point, VMobject, Group
|
from mobject.mobject import Mobject, Group
|
||||||
|
from mobject.vectorized_mobject import VMobject, VectorizedPoint
|
||||||
from topics.geometry import Dot, Circle
|
from topics.geometry import Dot, Circle
|
||||||
|
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.rate_functions import squish_rate_func
|
||||||
|
|
||||||
class Transform(Animation):
|
class Transform(Animation):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
|
|
@ -107,7 +113,7 @@ class GrowFromPoint(Transform):
|
||||||
def __init__(self, mobject, point, **kwargs):
|
def __init__(self, mobject, point, **kwargs):
|
||||||
digest_config(self, kwargs)
|
digest_config(self, kwargs)
|
||||||
target = mobject.copy()
|
target = mobject.copy()
|
||||||
point_mob = Point(point)
|
point_mob = VectorizedPoint(point)
|
||||||
if self.point_color:
|
if self.point_color:
|
||||||
point_mob.set_color(self.point_color)
|
point_mob.set_color(self.point_color)
|
||||||
mobject.replace(point_mob)
|
mobject.replace(point_mob)
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@ as a convenience for scripts createing scenes for videos
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from constants import *
|
from constants import *
|
||||||
from helpers import *
|
|
||||||
|
|
||||||
from animation.animation import *
|
from animation.animation import *
|
||||||
from animation.compositions import *
|
from animation.compositions import *
|
||||||
|
|
@ -59,6 +58,18 @@ from topics.probability import *
|
||||||
from topics.three_dimensions import *
|
from topics.three_dimensions import *
|
||||||
from topics.vector_space_scene import *
|
from topics.vector_space_scene import *
|
||||||
|
|
||||||
|
from utils.bezier import *
|
||||||
|
from utils.color import *
|
||||||
|
from utils.config_ops import *
|
||||||
|
from utils.images import *
|
||||||
|
from utils.iterables import *
|
||||||
|
from utils.paths import *
|
||||||
|
from utils.rate_functions import *
|
||||||
|
from utils.simple_functions import *
|
||||||
|
from utils.sounds import *
|
||||||
|
from utils.space_ops import *
|
||||||
|
from utils.strings import *
|
||||||
|
|
||||||
from special_animations import *
|
from special_animations import *
|
||||||
|
|
||||||
# Non manim libraries that are also nice to have without thinking
|
# Non manim libraries that are also nice to have without thinking
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
from camera import *
|
|
||||||
|
|
@ -6,13 +6,21 @@ from PIL import Image
|
||||||
from colour import Color
|
from colour import Color
|
||||||
import aggdraw
|
import aggdraw
|
||||||
import copy
|
import copy
|
||||||
|
|
||||||
from helpers import *
|
|
||||||
from mobject import Mobject, PMobject, VMobject, \
|
|
||||||
ImageMobject, Group
|
|
||||||
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
from constants import *
|
||||||
|
from mobject.mobject import Mobject, Group
|
||||||
|
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.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.simple_functions import fdiv
|
||||||
|
|
||||||
|
|
||||||
class Camera(object):
|
class Camera(object):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
"background_image" : None,
|
"background_image" : None,
|
||||||
|
|
|
||||||
12
constants.py
12
constants.py
|
|
@ -1,6 +1,12 @@
|
||||||
import os
|
import os
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
|
# Things anyone wishing to use this repository for their
|
||||||
|
# own use will want to change this
|
||||||
|
MEDIA_DIR = os.path.join(os.path.expanduser('~'), "Dropbox (3Blue1Brown)/3Blue1Brown Team Folder")
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_PIXEL_HEIGHT = 1080
|
DEFAULT_PIXEL_HEIGHT = 1080
|
||||||
DEFAULT_PIXEL_WIDTH = 1920
|
DEFAULT_PIXEL_WIDTH = 1920
|
||||||
|
|
||||||
|
|
@ -66,10 +72,6 @@ RIGHT_SIDE = FRAME_X_RADIUS*RIGHT
|
||||||
TAU = 2*np.pi
|
TAU = 2*np.pi
|
||||||
DEGREES = TAU/360
|
DEGREES = TAU/360
|
||||||
|
|
||||||
# Change this to point to where you want
|
|
||||||
# animation files to output
|
|
||||||
MEDIA_DIR = os.path.join(os.path.expanduser('~'), "Dropbox (3Blue1Brown)/3Blue1Brown Team Folder")
|
|
||||||
|
|
||||||
ANIMATIONS_DIR = os.path.join(MEDIA_DIR, "animations")
|
ANIMATIONS_DIR = os.path.join(MEDIA_DIR, "animations")
|
||||||
RASTER_IMAGE_DIR = os.path.join(MEDIA_DIR, "designs", "raster_images")
|
RASTER_IMAGE_DIR = os.path.join(MEDIA_DIR, "designs", "raster_images")
|
||||||
SVG_IMAGE_DIR = os.path.join(MEDIA_DIR, "designs", "svg_images")
|
SVG_IMAGE_DIR = os.path.join(MEDIA_DIR, "designs", "svg_images")
|
||||||
|
|
@ -100,13 +102,11 @@ TEX_TEXT_TO_REPLACE = "YourTextHere"
|
||||||
TEMPLATE_TEX_FILE = os.path.join(THIS_DIR, "template.tex")
|
TEMPLATE_TEX_FILE = os.path.join(THIS_DIR, "template.tex")
|
||||||
TEMPLATE_TEXT_FILE = os.path.join(THIS_DIR, "text_template.tex")
|
TEMPLATE_TEXT_FILE = os.path.join(THIS_DIR, "text_template.tex")
|
||||||
|
|
||||||
|
|
||||||
FFMPEG_BIN = "ffmpeg"
|
FFMPEG_BIN = "ffmpeg"
|
||||||
|
|
||||||
|
|
||||||
### Colors ###
|
### Colors ###
|
||||||
|
|
||||||
|
|
||||||
COLOR_MAP = {
|
COLOR_MAP = {
|
||||||
"DARK_BLUE" : "#236B8E",
|
"DARK_BLUE" : "#236B8E",
|
||||||
"DARK_BROWN" : "#8B4513",
|
"DARK_BROWN" : "#8B4513",
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
from container import *
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
from helpers import *
|
from constants import *
|
||||||
|
from utils.config_ops import digest_config
|
||||||
|
|
||||||
# Currently, this is only used by both Scene and MOBject.
|
# Currently, this is only used by both Scene and MOBject.
|
||||||
# Still, we abstract its functionality here, albeit purely nominally.
|
# Still, we abstract its functionality here, albeit purely nominally.
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,6 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
from helpers import *
|
from big_ol_pile_of_manim_imports import *
|
||||||
|
|
||||||
from mobject.tex_mobject import TexMobject
|
|
||||||
from mobject import Mobject
|
|
||||||
from mobject.image_mobject import ImageMobject
|
|
||||||
from mobject.vectorized_mobject import *
|
|
||||||
from mobject.svg_mobject import *
|
|
||||||
from mobject.tex_mobject import *
|
|
||||||
|
|
||||||
from scene import Scene
|
|
||||||
from camera import Camera
|
|
||||||
|
|
||||||
from animation.animation import Animation
|
|
||||||
from animation.transform import *
|
|
||||||
from animation.simple_animations import *
|
|
||||||
from animation.compositions import *
|
|
||||||
from animation.playground import *
|
|
||||||
|
|
||||||
from topics.geometry import *
|
|
||||||
from topics.characters import *
|
|
||||||
from topics.functions import *
|
|
||||||
from topics.number_line import *
|
|
||||||
from topics.combinatorics import *
|
|
||||||
from topics.three_dimensions import *
|
|
||||||
|
|
||||||
from topics.three_dimensions import *
|
|
||||||
|
|
||||||
# To watch one of these scenes, run the following:
|
# To watch one of these scenes, run the following:
|
||||||
# python extract_scene.py file_name <SceneName> -p
|
# python extract_scene.py file_name <SceneName> -p
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,10 @@ import imp
|
||||||
import os
|
import os
|
||||||
import subprocess as sp
|
import subprocess as sp
|
||||||
|
|
||||||
from helpers import *
|
from constants import *
|
||||||
from scene import Scene
|
from scene.scene import Scene
|
||||||
from camera import Camera
|
from camera.camera import Camera
|
||||||
|
from utils.sounds import play_error_sound, play_finish_sound
|
||||||
|
|
||||||
HELP_MESSAGE = """
|
HELP_MESSAGE = """
|
||||||
Usage:
|
Usage:
|
||||||
|
|
|
||||||
728
helpers.py
728
helpers.py
|
|
@ -1,728 +0,0 @@
|
||||||
import numpy as np
|
|
||||||
import itertools as it
|
|
||||||
import operator as op
|
|
||||||
from PIL import Image
|
|
||||||
from colour import Color
|
|
||||||
import random
|
|
||||||
import inspect
|
|
||||||
import string
|
|
||||||
import re
|
|
||||||
import os
|
|
||||||
from scipy import linalg
|
|
||||||
|
|
||||||
from constants import *
|
|
||||||
|
|
||||||
CLOSED_THRESHOLD = 0.01
|
|
||||||
STRAIGHT_PATH_THRESHOLD = 0.01
|
|
||||||
|
|
||||||
def play_chord(*nums):
|
|
||||||
commands = [
|
|
||||||
"play",
|
|
||||||
"-n",
|
|
||||||
"-c1",
|
|
||||||
"--no-show-progress",
|
|
||||||
"synth",
|
|
||||||
] + [
|
|
||||||
"sin %-"+str(num)
|
|
||||||
for num in nums
|
|
||||||
] + [
|
|
||||||
"fade h 0.5 1 0.5",
|
|
||||||
"> /dev/null"
|
|
||||||
]
|
|
||||||
try:
|
|
||||||
os.system(" ".join(commands))
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def play_error_sound():
|
|
||||||
play_chord(11, 8, 6, 1)
|
|
||||||
|
|
||||||
def play_finish_sound():
|
|
||||||
play_chord(12, 9, 5, 2)
|
|
||||||
|
|
||||||
def get_smooth_handle_points(points):
|
|
||||||
points = np.array(points)
|
|
||||||
num_handles = len(points) - 1
|
|
||||||
dim = points.shape[1]
|
|
||||||
if num_handles < 1:
|
|
||||||
return np.zeros((0, dim)), np.zeros((0, dim))
|
|
||||||
#Must solve 2*num_handles equations to get the handles.
|
|
||||||
#l and u are the number of lower an upper diagonal rows
|
|
||||||
#in the matrix to solve.
|
|
||||||
l, u = 2, 1
|
|
||||||
#diag is a representation of the matrix in diagonal form
|
|
||||||
#See https://www.particleincell.com/2012/bezier-splines/
|
|
||||||
#for how to arive at these equations
|
|
||||||
diag = np.zeros((l+u+1, 2*num_handles))
|
|
||||||
diag[0,1::2] = -1
|
|
||||||
diag[0,2::2] = 1
|
|
||||||
diag[1,0::2] = 2
|
|
||||||
diag[1,1::2] = 1
|
|
||||||
diag[2,1:-2:2] = -2
|
|
||||||
diag[3,0:-3:2] = 1
|
|
||||||
#last
|
|
||||||
diag[2,-2] = -1
|
|
||||||
diag[1,-1] = 2
|
|
||||||
#This is the b as in Ax = b, where we are solving for x,
|
|
||||||
#and A is represented using diag. However, think of entries
|
|
||||||
#to x and b as being points in space, not numbers
|
|
||||||
b = np.zeros((2*num_handles, dim))
|
|
||||||
b[1::2] = 2*points[1:]
|
|
||||||
b[0] = points[0]
|
|
||||||
b[-1] = points[-1]
|
|
||||||
solve_func = lambda b : linalg.solve_banded(
|
|
||||||
(l, u), diag, b
|
|
||||||
)
|
|
||||||
if is_closed(points):
|
|
||||||
#Get equations to relate first and last points
|
|
||||||
matrix = diag_to_matrix((l, u), diag)
|
|
||||||
#last row handles second derivative
|
|
||||||
matrix[-1, [0, 1, -2, -1]] = [2, -1, 1, -2]
|
|
||||||
#first row handles first derivative
|
|
||||||
matrix[0,:] = np.zeros(matrix.shape[1])
|
|
||||||
matrix[0,[0, -1]] = [1, 1]
|
|
||||||
b[0] = 2*points[0]
|
|
||||||
b[-1] = np.zeros(dim)
|
|
||||||
solve_func = lambda b : linalg.solve(matrix, b)
|
|
||||||
handle_pairs = np.zeros((2*num_handles, dim))
|
|
||||||
for i in range(dim):
|
|
||||||
handle_pairs[:,i] = solve_func(b[:,i])
|
|
||||||
return handle_pairs[0::2], handle_pairs[1::2]
|
|
||||||
|
|
||||||
def diag_to_matrix(l_and_u, diag):
|
|
||||||
"""
|
|
||||||
Converts array whose rows represent diagonal
|
|
||||||
entries of a matrix into the matrix itself.
|
|
||||||
See scipy.linalg.solve_banded
|
|
||||||
"""
|
|
||||||
l, u = l_and_u
|
|
||||||
dim = diag.shape[1]
|
|
||||||
matrix = np.zeros((dim, dim))
|
|
||||||
for i in range(l+u+1):
|
|
||||||
np.fill_diagonal(
|
|
||||||
matrix[max(0,i-u):,max(0,u-i):],
|
|
||||||
diag[i,max(0,u-i):]
|
|
||||||
)
|
|
||||||
return matrix
|
|
||||||
|
|
||||||
def is_closed(points):
|
|
||||||
return np.linalg.norm(points[0] - points[-1]) < CLOSED_THRESHOLD
|
|
||||||
|
|
||||||
## Color
|
|
||||||
|
|
||||||
def color_to_rgb(color):
|
|
||||||
return np.array(Color(color).get_rgb())
|
|
||||||
|
|
||||||
def color_to_rgba(color, alpha = 1):
|
|
||||||
return np.append(color_to_rgb(color), [alpha])
|
|
||||||
|
|
||||||
def rgb_to_color(rgb):
|
|
||||||
try:
|
|
||||||
return Color(rgb = rgb)
|
|
||||||
except:
|
|
||||||
return Color(WHITE)
|
|
||||||
|
|
||||||
def rgba_to_color(rgba):
|
|
||||||
return rgb_to_color(rgba[:3])
|
|
||||||
|
|
||||||
def rgb_to_hex(rgb):
|
|
||||||
return "#" + "".join('%02x'%int(255*x) for x in rgb)
|
|
||||||
|
|
||||||
def invert_color(color):
|
|
||||||
return rgb_to_color(1.0 - color_to_rgb(color))
|
|
||||||
|
|
||||||
def color_to_int_rgb(color):
|
|
||||||
return (255*color_to_rgb(color)).astype('uint8')
|
|
||||||
|
|
||||||
def color_to_int_rgba(color, alpha = 255):
|
|
||||||
return np.append(color_to_int_rgb(color), alpha)
|
|
||||||
|
|
||||||
def color_gradient(reference_colors, length_of_output):
|
|
||||||
if length_of_output == 0:
|
|
||||||
return reference_colors[0]
|
|
||||||
rgbs = map(color_to_rgb, reference_colors)
|
|
||||||
alphas = np.linspace(0, (len(rgbs) - 1), length_of_output)
|
|
||||||
floors = alphas.astype('int')
|
|
||||||
alphas_mod1 = alphas % 1
|
|
||||||
#End edge case
|
|
||||||
alphas_mod1[-1] = 1
|
|
||||||
floors[-1] = len(rgbs) - 2
|
|
||||||
return [
|
|
||||||
rgb_to_color(interpolate(rgbs[i], rgbs[i+1], alpha))
|
|
||||||
for i, alpha in zip(floors, alphas_mod1)
|
|
||||||
]
|
|
||||||
|
|
||||||
def interpolate_color(color1, color2, alpha):
|
|
||||||
rgb = interpolate(color_to_rgb(color1), color_to_rgb(color2), alpha)
|
|
||||||
return rgb_to_color(rgb)
|
|
||||||
|
|
||||||
def average_color(*colors):
|
|
||||||
rgbs = np.array(map(color_to_rgb, colors))
|
|
||||||
mean_rgb = np.apply_along_axis(np.mean, 0, rgbs)
|
|
||||||
return rgb_to_color(mean_rgb)
|
|
||||||
|
|
||||||
###
|
|
||||||
|
|
||||||
def compass_directions(n = 4, start_vect = RIGHT):
|
|
||||||
angle = 2*np.pi/n
|
|
||||||
return np.array([
|
|
||||||
rotate_vector(start_vect, k*angle)
|
|
||||||
for k in range(n)
|
|
||||||
])
|
|
||||||
|
|
||||||
def partial_bezier_points(points, a, b):
|
|
||||||
"""
|
|
||||||
Given an array of points which define
|
|
||||||
a bezier curve, and two numbers 0<=a<b<=1,
|
|
||||||
return an array of the same size, which
|
|
||||||
describes the portion of the original bezier
|
|
||||||
curve on the interval [a, b].
|
|
||||||
|
|
||||||
This algorithm is pretty nifty, and pretty dense.
|
|
||||||
"""
|
|
||||||
a_to_1 = np.array([
|
|
||||||
bezier(points[i:])(a)
|
|
||||||
for i in range(len(points))
|
|
||||||
])
|
|
||||||
return np.array([
|
|
||||||
bezier(a_to_1[:i+1])((b-a)/(1.-a))
|
|
||||||
for i in range(len(points))
|
|
||||||
])
|
|
||||||
|
|
||||||
def bezier(points):
|
|
||||||
n = len(points) - 1
|
|
||||||
return lambda t : sum([
|
|
||||||
((1-t)**(n-k))*(t**k)*choose(n, k)*point
|
|
||||||
for k, point in enumerate(points)
|
|
||||||
])
|
|
||||||
|
|
||||||
def remove_list_redundancies(l):
|
|
||||||
"""
|
|
||||||
Used instead of list(set(l)) to maintain order
|
|
||||||
Keeps the last occurance of each element
|
|
||||||
"""
|
|
||||||
reversed_result = []
|
|
||||||
used = set()
|
|
||||||
for x in reversed(l):
|
|
||||||
if not x in used:
|
|
||||||
reversed_result.append(x)
|
|
||||||
used.add(x)
|
|
||||||
reversed_result.reverse()
|
|
||||||
return reversed_result
|
|
||||||
|
|
||||||
def list_update(l1, l2):
|
|
||||||
"""
|
|
||||||
Used instead of list(set(l1).update(l2)) to maintain order,
|
|
||||||
making sure duplicates are removed from l1, not l2.
|
|
||||||
"""
|
|
||||||
return filter(lambda e : e not in l2, l1) + list(l2)
|
|
||||||
|
|
||||||
def list_difference_update(l1, l2):
|
|
||||||
return filter(lambda e : e not in l2, l1)
|
|
||||||
|
|
||||||
def all_elements_are_instances(iterable, Class):
|
|
||||||
return all(map(lambda e : isinstance(e, Class), iterable))
|
|
||||||
|
|
||||||
def adjacent_pairs(objects):
|
|
||||||
return zip(objects, list(objects[1:])+[objects[0]])
|
|
||||||
|
|
||||||
def batch_by_property(items, property_func):
|
|
||||||
"""
|
|
||||||
Takes in a list, and returns a list of tuples, (batch, prop)
|
|
||||||
such that all items in a batch have the same output when
|
|
||||||
put into property_func, and such that chaining all these
|
|
||||||
batches together would give the original list.
|
|
||||||
"""
|
|
||||||
batch_prop_pairs = []
|
|
||||||
def add_batch_prop_pair(batch):
|
|
||||||
if len(batch) > 0:
|
|
||||||
batch_prop_pairs.append(
|
|
||||||
(batch, property_func(batch[0]))
|
|
||||||
)
|
|
||||||
curr_batch = []
|
|
||||||
curr_prop = None
|
|
||||||
for item in items:
|
|
||||||
prop = property_func(item)
|
|
||||||
if prop != curr_prop:
|
|
||||||
add_batch_prop_pair(curr_batch)
|
|
||||||
curr_prop = prop
|
|
||||||
curr_batch = [item]
|
|
||||||
else:
|
|
||||||
curr_batch.append(item)
|
|
||||||
add_batch_prop_pair(curr_batch)
|
|
||||||
return batch_prop_pairs
|
|
||||||
|
|
||||||
def complex_to_R3(complex_num):
|
|
||||||
return np.array((complex_num.real, complex_num.imag, 0))
|
|
||||||
|
|
||||||
def R3_to_complex(point):
|
|
||||||
return complex(*point[:2])
|
|
||||||
|
|
||||||
def tuplify(obj):
|
|
||||||
if isinstance(obj, str):
|
|
||||||
return (obj,)
|
|
||||||
try:
|
|
||||||
return tuple(obj)
|
|
||||||
except:
|
|
||||||
return (obj,)
|
|
||||||
|
|
||||||
def instantiate(obj):
|
|
||||||
"""
|
|
||||||
Useful so that classes or instance of those classes can be
|
|
||||||
included in configuration, which can prevent defaults from
|
|
||||||
getting created during compilation/importing
|
|
||||||
"""
|
|
||||||
return obj() if isinstance(obj, type) else obj
|
|
||||||
|
|
||||||
def get_all_descendent_classes(Class):
|
|
||||||
awaiting_review = [Class]
|
|
||||||
result = []
|
|
||||||
while awaiting_review:
|
|
||||||
Child = awaiting_review.pop()
|
|
||||||
awaiting_review += Child.__subclasses__()
|
|
||||||
result.append(Child)
|
|
||||||
return result
|
|
||||||
|
|
||||||
def filtered_locals(caller_locals):
|
|
||||||
result = caller_locals.copy()
|
|
||||||
ignored_local_args = ["self", "kwargs"]
|
|
||||||
for arg in ignored_local_args:
|
|
||||||
result.pop(arg, caller_locals)
|
|
||||||
return result
|
|
||||||
|
|
||||||
def digest_config(obj, kwargs, caller_locals = {}):
|
|
||||||
"""
|
|
||||||
Sets init args and CONFIG values as local variables
|
|
||||||
|
|
||||||
The purpose of this function is to ensure that all
|
|
||||||
configuration of any object is inheritable, able to
|
|
||||||
be easily passed into instantiation, and is attached
|
|
||||||
as an attribute of the object.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Assemble list of CONFIGs from all super classes
|
|
||||||
classes_in_hierarchy = [obj.__class__]
|
|
||||||
static_configs = []
|
|
||||||
while len(classes_in_hierarchy) > 0:
|
|
||||||
Class = classes_in_hierarchy.pop()
|
|
||||||
classes_in_hierarchy += Class.__bases__
|
|
||||||
if hasattr(Class, "CONFIG"):
|
|
||||||
static_configs.append(Class.CONFIG)
|
|
||||||
|
|
||||||
#Order matters a lot here, first dicts have higher priority
|
|
||||||
caller_locals = filtered_locals(caller_locals)
|
|
||||||
all_dicts = [kwargs, caller_locals, obj.__dict__]
|
|
||||||
all_dicts += static_configs
|
|
||||||
all_new_dicts = [kwargs, caller_locals] + static_configs
|
|
||||||
obj.__dict__ = merge_config(all_dicts)
|
|
||||||
#Keep track of the configuration of objects upon
|
|
||||||
#instantiation
|
|
||||||
obj.initial_config = merge_config(all_new_dicts)
|
|
||||||
|
|
||||||
def merge_config(all_dicts):
|
|
||||||
all_config = reduce(op.add, [d.items() for d in all_dicts])
|
|
||||||
config = dict()
|
|
||||||
for c in all_config:
|
|
||||||
key, value = c
|
|
||||||
if not key in config:
|
|
||||||
config[key] = value
|
|
||||||
else:
|
|
||||||
#When two dictionaries have the same key, they are merged.
|
|
||||||
if isinstance(value, dict) and isinstance(config[key], dict):
|
|
||||||
config[key] = merge_config([config[key], value])
|
|
||||||
return config
|
|
||||||
|
|
||||||
def soft_dict_update(d1, d2):
|
|
||||||
"""
|
|
||||||
Adds key values pairs of d2 to d1 only when d1 doesn't
|
|
||||||
already have that key
|
|
||||||
"""
|
|
||||||
for key, value in d2.items():
|
|
||||||
if key not in d1:
|
|
||||||
d1[key] = value
|
|
||||||
|
|
||||||
def digest_locals(obj, keys = None):
|
|
||||||
caller_locals = filtered_locals(
|
|
||||||
inspect.currentframe().f_back.f_locals
|
|
||||||
)
|
|
||||||
if keys is None:
|
|
||||||
keys = caller_locals.keys()
|
|
||||||
for key in keys:
|
|
||||||
setattr(obj, key, caller_locals[key])
|
|
||||||
|
|
||||||
def interpolate(start, end, alpha):
|
|
||||||
return (1-alpha)*start + alpha*end
|
|
||||||
|
|
||||||
def mid(start, end):
|
|
||||||
return (start + end)/2.0
|
|
||||||
|
|
||||||
def inverse_interpolate(start, end, value):
|
|
||||||
return np.true_divide(value - start, end - start)
|
|
||||||
|
|
||||||
def match_interpolate(new_start, new_end, old_start, old_end, old_value):
|
|
||||||
return interpolate(
|
|
||||||
new_start, new_end,
|
|
||||||
inverse_interpolate(old_start, old_end, old_value))
|
|
||||||
|
|
||||||
def clamp(lower, upper, val):
|
|
||||||
if val < lower:
|
|
||||||
return lower
|
|
||||||
elif val > upper:
|
|
||||||
return upper
|
|
||||||
return val
|
|
||||||
|
|
||||||
def center_of_mass(points):
|
|
||||||
points = [np.array(point).astype("float") for point in points]
|
|
||||||
return sum(points) / len(points)
|
|
||||||
|
|
||||||
def choose(n, r):
|
|
||||||
if n < r: return 0
|
|
||||||
if r == 0: return 1
|
|
||||||
denom = reduce(op.mul, xrange(1, r+1), 1)
|
|
||||||
numer = reduce(op.mul, xrange(n, n-r, -1), 1)
|
|
||||||
return numer//denom
|
|
||||||
|
|
||||||
def is_on_line(p0, p1, p2, threshold = 0.01):
|
|
||||||
"""
|
|
||||||
Returns true of p0 is on the line between p1 and p2
|
|
||||||
"""
|
|
||||||
p0, p1, p2 = map(lambda tup : np.array(tup[:2]), [p0, p1, p2])
|
|
||||||
p1 -= p0
|
|
||||||
p2 -= p0
|
|
||||||
return abs((p1[0] / p1[1]) - (p2[0] / p2[1])) < threshold
|
|
||||||
|
|
||||||
|
|
||||||
def intersection(line1, line2):
|
|
||||||
"""
|
|
||||||
A "line" should come in the form [(x0, y0), (x1, y1)] for two
|
|
||||||
points it runs through
|
|
||||||
"""
|
|
||||||
p0, p1, p2, p3 = map(
|
|
||||||
lambda tup : np.array(tup[:2]),
|
|
||||||
[line1[0], line1[1], line2[0], line2[1]]
|
|
||||||
)
|
|
||||||
p1, p2, p3 = map(lambda x : x - p0, [p1, p2, p3])
|
|
||||||
transform = np.zeros((2, 2))
|
|
||||||
transform[:,0], transform[:,1] = p1, p2
|
|
||||||
if np.linalg.det(transform) == 0: return
|
|
||||||
inv = np.linalg.inv(transform)
|
|
||||||
new_p3 = np.dot(inv, p3.reshape((2, 1)))
|
|
||||||
#Where does line connecting (0, 1) to new_p3 hit x axis
|
|
||||||
x_intercept = new_p3[0] / (1 - new_p3[1])
|
|
||||||
result = np.dot(transform, [[x_intercept], [0]])
|
|
||||||
result = result.reshape((2,)) + p0
|
|
||||||
return result
|
|
||||||
|
|
||||||
def random_bright_color():
|
|
||||||
color = random_color()
|
|
||||||
curr_rgb = color_to_rgb(color)
|
|
||||||
new_rgb = interpolate(
|
|
||||||
curr_rgb, np.ones(len(curr_rgb)), 0.5
|
|
||||||
)
|
|
||||||
return Color(rgb = new_rgb)
|
|
||||||
|
|
||||||
def random_color():
|
|
||||||
return random.choice(PALETTE)
|
|
||||||
|
|
||||||
|
|
||||||
################################################
|
|
||||||
|
|
||||||
def straight_path(start_points, end_points, alpha):
|
|
||||||
return interpolate(start_points, end_points, alpha)
|
|
||||||
|
|
||||||
def path_along_arc(arc_angle, axis = OUT):
|
|
||||||
"""
|
|
||||||
If vect is vector from start to end, [vect[:,1], -vect[:,0]] is
|
|
||||||
perpendicular to vect in the left direction.
|
|
||||||
"""
|
|
||||||
if abs(arc_angle) < STRAIGHT_PATH_THRESHOLD:
|
|
||||||
return straight_path
|
|
||||||
if np.linalg.norm(axis) == 0:
|
|
||||||
axis = OUT
|
|
||||||
unit_axis = axis/np.linalg.norm(axis)
|
|
||||||
def path(start_points, end_points, alpha):
|
|
||||||
vects = end_points - start_points
|
|
||||||
centers = start_points + 0.5*vects
|
|
||||||
if arc_angle != np.pi:
|
|
||||||
centers += np.cross(unit_axis, vects/2.0)/np.tan(arc_angle/2)
|
|
||||||
rot_matrix = rotation_matrix(alpha*arc_angle, unit_axis)
|
|
||||||
return centers + np.dot(start_points-centers, rot_matrix.T)
|
|
||||||
return path
|
|
||||||
|
|
||||||
def clockwise_path():
|
|
||||||
return path_along_arc(-np.pi)
|
|
||||||
|
|
||||||
def counterclockwise_path():
|
|
||||||
return path_along_arc(np.pi)
|
|
||||||
|
|
||||||
|
|
||||||
################################################
|
|
||||||
|
|
||||||
def to_camel_case(name):
|
|
||||||
return "".join([
|
|
||||||
filter(
|
|
||||||
lambda c : c not in string.punctuation + string.whitespace, part
|
|
||||||
).capitalize()
|
|
||||||
for part in name.split("_")
|
|
||||||
])
|
|
||||||
|
|
||||||
def initials(name, sep_values = [" ", "_"]):
|
|
||||||
return "".join([
|
|
||||||
(s[0] if s else "")
|
|
||||||
for s in re.split("|".join(sep_values), name)
|
|
||||||
])
|
|
||||||
|
|
||||||
def camel_case_initials(name):
|
|
||||||
return filter(lambda c : c.isupper(), name)
|
|
||||||
|
|
||||||
################################################
|
|
||||||
|
|
||||||
def get_full_raster_image_path(image_file_name):
|
|
||||||
possible_paths = [
|
|
||||||
image_file_name,
|
|
||||||
os.path.join(RASTER_IMAGE_DIR, image_file_name),
|
|
||||||
os.path.join(RASTER_IMAGE_DIR, image_file_name + ".jpg"),
|
|
||||||
os.path.join(RASTER_IMAGE_DIR, image_file_name + ".png"),
|
|
||||||
os.path.join(RASTER_IMAGE_DIR, image_file_name + ".gif"),
|
|
||||||
]
|
|
||||||
for path in possible_paths:
|
|
||||||
if os.path.exists(path):
|
|
||||||
return path
|
|
||||||
raise IOError("File %s not Found"%image_file_name)
|
|
||||||
|
|
||||||
def drag_pixels(frames):
|
|
||||||
curr = frames[0]
|
|
||||||
new_frames = []
|
|
||||||
for frame in frames:
|
|
||||||
curr += (curr == 0) * np.array(frame)
|
|
||||||
new_frames.append(np.array(curr))
|
|
||||||
return new_frames
|
|
||||||
|
|
||||||
def invert_image(image):
|
|
||||||
arr = np.array(image)
|
|
||||||
arr = (255 * np.ones(arr.shape)).astype(arr.dtype) - arr
|
|
||||||
return Image.fromarray(arr)
|
|
||||||
|
|
||||||
def stretch_array_to_length(nparray, length):
|
|
||||||
curr_len = len(nparray)
|
|
||||||
if curr_len > length:
|
|
||||||
raise Warning("Trying to stretch array to a length shorter than its own")
|
|
||||||
indices = np.arange(length)/ float(length)
|
|
||||||
indices *= curr_len
|
|
||||||
return nparray[indices.astype('int')]
|
|
||||||
|
|
||||||
def make_even(iterable_1, iterable_2):
|
|
||||||
list_1, list_2 = list(iterable_1), list(iterable_2)
|
|
||||||
length = max(len(list_1), len(list_2))
|
|
||||||
return (
|
|
||||||
[list_1[(n * len(list_1)) / length] for n in xrange(length)],
|
|
||||||
[list_2[(n * len(list_2)) / length] for n in xrange(length)]
|
|
||||||
)
|
|
||||||
|
|
||||||
def make_even_by_cycling(iterable_1, iterable_2):
|
|
||||||
length = max(len(iterable_1), len(iterable_2))
|
|
||||||
cycle1 = it.cycle(iterable_1)
|
|
||||||
cycle2 = it.cycle(iterable_2)
|
|
||||||
return (
|
|
||||||
[cycle1.next() for x in range(length)],
|
|
||||||
[cycle2.next() for x in range(length)]
|
|
||||||
)
|
|
||||||
|
|
||||||
### Rate Functions ###
|
|
||||||
|
|
||||||
def sigmoid(x):
|
|
||||||
return 1.0/(1 + np.exp(-x))
|
|
||||||
|
|
||||||
def smooth(t, inflection = 10.0):
|
|
||||||
error = sigmoid(-inflection / 2)
|
|
||||||
return (sigmoid(inflection*(t - 0.5)) - error) / (1 - 2*error)
|
|
||||||
|
|
||||||
def rush_into(t):
|
|
||||||
return 2*smooth(t/2.0)
|
|
||||||
|
|
||||||
def rush_from(t):
|
|
||||||
return 2*smooth(t/2.0+0.5) - 1
|
|
||||||
|
|
||||||
def slow_into(t):
|
|
||||||
return np.sqrt(1-(1-t)*(1-t))
|
|
||||||
|
|
||||||
def double_smooth(t):
|
|
||||||
if t < 0.5:
|
|
||||||
return 0.5*smooth(2*t)
|
|
||||||
else:
|
|
||||||
return 0.5*(1 + smooth(2*t - 1))
|
|
||||||
|
|
||||||
def there_and_back(t, inflection = 10.0):
|
|
||||||
new_t = 2*t if t < 0.5 else 2*(1 - t)
|
|
||||||
return smooth(new_t, inflection)
|
|
||||||
|
|
||||||
def there_and_back_with_pause(t):
|
|
||||||
if t < 1./3:
|
|
||||||
return smooth(3*t)
|
|
||||||
elif t < 2./3:
|
|
||||||
return 1
|
|
||||||
else:
|
|
||||||
return smooth(3 - 3*t)
|
|
||||||
|
|
||||||
def running_start(t, pull_factor = -0.5):
|
|
||||||
return bezier([0, 0, pull_factor, pull_factor, 1, 1, 1])(t)
|
|
||||||
|
|
||||||
def not_quite_there(func = smooth, proportion = 0.7):
|
|
||||||
def result(t):
|
|
||||||
return proportion*func(t)
|
|
||||||
return result
|
|
||||||
|
|
||||||
def wiggle(t, wiggles = 2):
|
|
||||||
return there_and_back(t) * np.sin(wiggles*np.pi*t)
|
|
||||||
|
|
||||||
def squish_rate_func(func, a = 0.4, b = 0.6):
|
|
||||||
def result(t):
|
|
||||||
if a == b:
|
|
||||||
return a
|
|
||||||
|
|
||||||
if t < a:
|
|
||||||
return func(0)
|
|
||||||
elif t > b:
|
|
||||||
return func(1)
|
|
||||||
else:
|
|
||||||
return func((t-a)/(b-a))
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
# Stylistically, should this take parameters (with default values)?
|
|
||||||
# Ultimately, the functionality is entirely subsumed by squish_rate_func,
|
|
||||||
# but it may be useful to have a nice name for with nice default params for
|
|
||||||
# "lingering", different from squish_rate_func's default params
|
|
||||||
def lingering(t):
|
|
||||||
return squish_rate_func(lambda t: t, 0, 0.8)(t)
|
|
||||||
|
|
||||||
### Functional Functions ###
|
|
||||||
|
|
||||||
def composition(func_list):
|
|
||||||
"""
|
|
||||||
func_list should contain elements of the form (f, args)
|
|
||||||
"""
|
|
||||||
return reduce(
|
|
||||||
lambda (f1, args1), (f2, args2) : (lambda x : f1(f2(x, *args2), *args1)),
|
|
||||||
func_list,
|
|
||||||
lambda x : x
|
|
||||||
)
|
|
||||||
|
|
||||||
def remove_nones(sequence):
|
|
||||||
return filter(lambda x : x, sequence)
|
|
||||||
|
|
||||||
#Matrix operations
|
|
||||||
def thick_diagonal(dim, thickness = 2):
|
|
||||||
row_indices = np.arange(dim).repeat(dim).reshape((dim, dim))
|
|
||||||
col_indices = np.transpose(row_indices)
|
|
||||||
return (np.abs(row_indices - col_indices)<thickness).astype('uint8')
|
|
||||||
|
|
||||||
def rotation_matrix(angle, axis):
|
|
||||||
"""
|
|
||||||
Rotation in R^3 about a specified axis of rotation.
|
|
||||||
"""
|
|
||||||
about_z = rotation_about_z(angle)
|
|
||||||
z_to_axis = z_to_vector(axis)
|
|
||||||
axis_to_z = np.linalg.inv(z_to_axis)
|
|
||||||
return reduce(np.dot, [z_to_axis, about_z, axis_to_z])
|
|
||||||
|
|
||||||
def rotation_about_z(angle):
|
|
||||||
return [
|
|
||||||
[np.cos(angle), -np.sin(angle), 0],
|
|
||||||
[np.sin(angle), np.cos(angle), 0],
|
|
||||||
[0, 0, 1]
|
|
||||||
]
|
|
||||||
|
|
||||||
def z_to_vector(vector):
|
|
||||||
"""
|
|
||||||
Returns some matrix in SO(3) which takes the z-axis to the
|
|
||||||
(normalized) vector provided as an argument
|
|
||||||
"""
|
|
||||||
norm = np.linalg.norm(vector)
|
|
||||||
if norm == 0:
|
|
||||||
return np.identity(3)
|
|
||||||
v = np.array(vector) / norm
|
|
||||||
phi = np.arccos(v[2])
|
|
||||||
if any(v[:2]):
|
|
||||||
#projection of vector to unit circle
|
|
||||||
axis_proj = v[:2] / np.linalg.norm(v[:2])
|
|
||||||
theta = np.arccos(axis_proj[0])
|
|
||||||
if axis_proj[1] < 0:
|
|
||||||
theta = -theta
|
|
||||||
else:
|
|
||||||
theta = 0
|
|
||||||
phi_down = np.array([
|
|
||||||
[np.cos(phi), 0, np.sin(phi)],
|
|
||||||
[0, 1, 0],
|
|
||||||
[-np.sin(phi), 0, np.cos(phi)]
|
|
||||||
])
|
|
||||||
return np.dot(rotation_about_z(theta), phi_down)
|
|
||||||
|
|
||||||
def rotate_vector(vector, angle, axis = OUT):
|
|
||||||
return np.dot(rotation_matrix(angle, axis), vector)
|
|
||||||
|
|
||||||
def angle_between(v1, v2):
|
|
||||||
return np.arccos(np.dot(
|
|
||||||
v1 / np.linalg.norm(v1),
|
|
||||||
v2 / np.linalg.norm(v2)
|
|
||||||
))
|
|
||||||
|
|
||||||
def angle_of_vector(vector):
|
|
||||||
"""
|
|
||||||
Returns polar coordinate theta when vector is project on xy plane
|
|
||||||
"""
|
|
||||||
z = complex(*vector[:2])
|
|
||||||
if z == 0:
|
|
||||||
return 0
|
|
||||||
return np.angle(complex(*vector[:2]))
|
|
||||||
|
|
||||||
def angle_between_vectors(v1, v2):
|
|
||||||
"""
|
|
||||||
Returns the angle between two 3D vectors.
|
|
||||||
This angle will always be btw 0 and TAU/2.
|
|
||||||
"""
|
|
||||||
l1 = np.linalg.norm(v1)
|
|
||||||
l2 = np.linalg.norm(v2)
|
|
||||||
return np.arccos(np.dot(v1,v2)/(l1*l2))
|
|
||||||
|
|
||||||
def project_along_vector(point, vector):
|
|
||||||
matrix = np.identity(3) - np.outer(vector, vector)
|
|
||||||
return np.dot(point, matrix.T)
|
|
||||||
|
|
||||||
def concatenate_lists(*list_of_lists):
|
|
||||||
return [item for l in list_of_lists for item in l]
|
|
||||||
|
|
||||||
# Occasionally convenient in order to write dict.x instead of more laborious
|
|
||||||
# (and less in keeping with all other attr accesses) dict["x"]
|
|
||||||
class DictAsObject(object):
|
|
||||||
def __init__(self, dict):
|
|
||||||
self.__dict__ = dict
|
|
||||||
|
|
||||||
# Just to have a less heavyweight name for this extremely common operation
|
|
||||||
#
|
|
||||||
# We may wish to have more fine-grained control over division by zero behavior
|
|
||||||
# in the future (separate specifiable values for 0/0 and x/0 with x != 0),
|
|
||||||
# but for now, we just allow the option to handle indeterminate 0/0.
|
|
||||||
def fdiv(a, b, zero_over_zero_value = None):
|
|
||||||
if zero_over_zero_value != None:
|
|
||||||
out = np.full_like(a, zero_over_zero_value)
|
|
||||||
where = np.logical_or (a != 0, b != 0)
|
|
||||||
else:
|
|
||||||
out = None
|
|
||||||
where = True
|
|
||||||
|
|
||||||
return np.true_divide(a, b, out = out, where = where)
|
|
||||||
|
|
||||||
def add_extension_if_not_present(file_name, extension):
|
|
||||||
# This could conceivably be smarter about handling existing differing extensions
|
|
||||||
if(file_name[-len(extension):] != extension):
|
|
||||||
return file_name + extension
|
|
||||||
else:
|
|
||||||
return file_name
|
|
||||||
|
|
||||||
# For debugging purposes
|
|
||||||
|
|
||||||
def print_mobject_family(mob, n_tabs = 0):
|
|
||||||
print "\t"*n_tabs, mob, id(mob)
|
|
||||||
for submob in mob.submobjects:
|
|
||||||
print_mobject_family(submob, n_tabs + 1)
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
__all__ = [
|
|
||||||
"mobject",
|
|
||||||
"image_mobject",
|
|
||||||
"tex_mobject",
|
|
||||||
]
|
|
||||||
|
|
||||||
from mobject import Mobject, Group
|
|
||||||
from point_cloud_mobject import Point, Mobject1D, Mobject2D, PMobject
|
|
||||||
from vectorized_mobject import VMobject, VGroup
|
|
||||||
from image_mobject import ImageMobject
|
|
||||||
|
|
@ -4,9 +4,14 @@ import os
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
from random import random
|
from random import random
|
||||||
|
|
||||||
from helpers import *
|
from constants import *
|
||||||
from mobject import Mobject
|
from .mobject import Mobject
|
||||||
from point_cloud_mobject import PMobject
|
from point_cloud_mobject import PMobject
|
||||||
|
from utils.bezier import interpolate
|
||||||
|
from utils.color import color_to_int_rgb
|
||||||
|
from utils.color import interpolate_color
|
||||||
|
from utils.config_ops import digest_config
|
||||||
|
from utils.images import get_full_raster_image_path
|
||||||
|
|
||||||
class ImageMobject(Mobject):
|
class ImageMobject(Mobject):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,21 @@
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import operator as op
|
import operator as op
|
||||||
|
import itertools as it
|
||||||
import os
|
import os
|
||||||
import copy
|
import copy
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
from colour import Color
|
from colour import Color
|
||||||
|
|
||||||
from helpers import *
|
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 interpolate_color
|
||||||
|
from utils.iterables import remove_list_redundancies, list_update
|
||||||
|
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 container import *
|
|
||||||
|
|
||||||
#TODO: Explain array_attrs
|
#TODO: Explain array_attrs
|
||||||
|
|
||||||
|
|
@ -87,7 +95,7 @@ class Mobject(Container):
|
||||||
|
|
||||||
def get_image(self, camera = None):
|
def get_image(self, camera = None):
|
||||||
if camera is None:
|
if camera is None:
|
||||||
from camera import Camera
|
from camera.camera import Camera
|
||||||
camera = Camera()
|
camera = Camera()
|
||||||
camera.capture_mobject(self)
|
camera.capture_mobject(self)
|
||||||
return camera.get_image()
|
return camera.get_image()
|
||||||
|
|
@ -750,6 +758,12 @@ class Mobject(Container):
|
||||||
)
|
)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
def print_submobject_family(self, n_tabs = 0):
|
||||||
|
"""For debugging purposes"""
|
||||||
|
print "\t"*n_tabs, self, id(self)
|
||||||
|
for submob in self.submobjects:
|
||||||
|
submob.print_mobject_family(n_tabs + 1)
|
||||||
|
|
||||||
## Alignment
|
## Alignment
|
||||||
def align_data(self, mobject):
|
def align_data(self, mobject):
|
||||||
self.align_submobjects(mobject)
|
self.align_submobjects(mobject)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,11 @@
|
||||||
|
from constants import *
|
||||||
from .mobject import Mobject
|
from .mobject import Mobject
|
||||||
from helpers import *
|
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 interpolate_color
|
||||||
|
from utils.config_ops import digest_config
|
||||||
|
from utils.iterables import stretch_array_to_length
|
||||||
|
|
||||||
class PMobject(Mobject):
|
class PMobject(Mobject):
|
||||||
def init_points(self):
|
def init_points(self):
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,10 @@ import itertools as it
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
|
|
||||||
from mobject import Mobject
|
from mobject.mobject import Mobject
|
||||||
|
from utils.iterables import adjacent_pairs
|
||||||
|
|
||||||
from helpers import *
|
from constants import *
|
||||||
|
|
||||||
#TODO, this whole class should be something vectorized.
|
#TODO, this whole class should be something vectorized.
|
||||||
class Region(Mobject):
|
class Region(Mobject):
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,14 @@
|
||||||
from xml.dom import minidom
|
from xml.dom import minidom
|
||||||
|
import itertools as it
|
||||||
|
import re
|
||||||
import warnings
|
import warnings
|
||||||
|
import string
|
||||||
|
|
||||||
|
from constants import *
|
||||||
from vectorized_mobject import VMobject, VGroup
|
from vectorized_mobject import VMobject, VGroup
|
||||||
from topics.geometry import Rectangle, Circle
|
from topics.geometry import Rectangle, Circle
|
||||||
from helpers import *
|
from utils.bezier import is_closed
|
||||||
|
from utils.config_ops import digest_config, digest_locals
|
||||||
|
|
||||||
def string_to_numbers(num_string):
|
def string_to_numbers(num_string):
|
||||||
num_string = num_string.replace("-",",-")
|
num_string = num_string.replace("-",",-")
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
from helpers import *
|
from constants import *
|
||||||
|
|
||||||
from vectorized_mobject import VMobject, VGroup, VectorizedPoint
|
from vectorized_mobject import VMobject, VGroup, VectorizedPoint
|
||||||
from svg_mobject import SVGMobject, VMobjectFromSVGPathstring
|
from svg_mobject import SVGMobject, VMobjectFromSVGPathstring
|
||||||
from topics.geometry import BackgroundRectangle
|
from topics.geometry import BackgroundRectangle
|
||||||
|
from utils.config_ops import digest_config
|
||||||
|
|
||||||
import collections
|
import collections
|
||||||
import sys
|
import sys
|
||||||
|
import operator as op
|
||||||
|
|
||||||
TEX_MOB_SCALE_FACTOR = 0.05
|
TEX_MOB_SCALE_FACTOR = 0.05
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,15 @@
|
||||||
import re
|
import re
|
||||||
|
from colour import Color
|
||||||
|
|
||||||
|
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.color import color_to_rgb
|
||||||
|
from utils.color import interpolate_color
|
||||||
|
from utils.iterables import make_even
|
||||||
|
|
||||||
from mobject import Mobject
|
|
||||||
|
|
||||||
from helpers import *
|
|
||||||
|
|
||||||
class VMobject(Mobject):
|
class VMobject(Mobject):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
|
|
|
||||||
0
old_projects/basel/__init__.py
Normal file
0
old_projects/basel/__init__.py
Normal file
|
|
@ -1,9 +1,9 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
from helpers import *
|
from constants import *
|
||||||
|
|
||||||
from mobject.tex_mobject import TexMobject
|
from mobject.tex_mobject import TexMobject
|
||||||
from mobject import Mobject
|
from mobject.mobject import Mobject
|
||||||
from mobject.image_mobject import ImageMobject
|
from mobject.image_mobject import ImageMobject
|
||||||
from mobject.vectorized_mobject import *
|
from mobject.vectorized_mobject import *
|
||||||
|
|
||||||
|
|
@ -19,8 +19,8 @@ from topics.characters import *
|
||||||
from topics.functions import *
|
from topics.functions import *
|
||||||
from topics.number_line import *
|
from topics.number_line import *
|
||||||
from topics.numerals import *
|
from topics.numerals import *
|
||||||
from scene import Scene
|
from scene.scene import Scene
|
||||||
from camera import Camera
|
from camera.camera import Camera
|
||||||
from mobject.svg_mobject import *
|
from mobject.svg_mobject import *
|
||||||
from mobject.tex_mobject import *
|
from mobject.tex_mobject import *
|
||||||
from topics.three_dimensions import *
|
from topics.three_dimensions import *
|
||||||
|
|
|
||||||
|
|
@ -1,35 +1,7 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from helpers import *
|
from big_ol_pile_of_manim_imports import *
|
||||||
|
|
||||||
from mobject.tex_mobject import TexMobject
|
|
||||||
from 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 topics.combinatorics import *
|
|
||||||
from scene import Scene
|
|
||||||
from scene.zoomed_scene import *
|
|
||||||
from camera import Camera
|
|
||||||
from mobject.svg_mobject import *
|
|
||||||
from mobject.tex_mobject import *
|
|
||||||
from topics.three_dimensions import *
|
|
||||||
from topics.light import *
|
|
||||||
from topics.objects import *
|
|
||||||
from topics.common_scenes import *
|
|
||||||
|
|
||||||
import types
|
import types
|
||||||
import functools
|
import functools
|
||||||
|
|
@ -260,7 +232,7 @@ class ThreeDSpotlight(VGroup):
|
||||||
fill_opacity = self.ambient_light.opacity_function(a1*distance),
|
fill_opacity = self.ambient_light.opacity_function(a1*distance),
|
||||||
stroke_width = 0
|
stroke_width = 0
|
||||||
))
|
))
|
||||||
|
|
||||||
class ContinualThreeDLightConeUpdate(ContinualAnimation):
|
class ContinualThreeDLightConeUpdate(ContinualAnimation):
|
||||||
def update(self, dt):
|
def update(self, dt):
|
||||||
self.mobject.update()
|
self.mobject.update()
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ from mobject import *
|
||||||
from constants import *
|
from constants import *
|
||||||
from mobject.region import *
|
from mobject.region import *
|
||||||
import displayer as disp
|
import displayer as disp
|
||||||
from scene import Scene, GraphScene
|
from scene.scene import Scene, GraphScene
|
||||||
from scene.graphs import *
|
from scene.graphs import *
|
||||||
from moser_main import EulersFormula
|
from moser_main import EulersFormula
|
||||||
from script_wrapper import command_line_create_scene
|
from script_wrapper import command_line_create_scene
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from helpers import *
|
from constants import *
|
||||||
import scipy
|
import scipy
|
||||||
|
|
||||||
from big_ol_pile_of_manim_imports import *
|
from big_ol_pile_of_manim_imports import *
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ from hilbert.curves import \
|
||||||
SnakeCurve
|
SnakeCurve
|
||||||
|
|
||||||
|
|
||||||
from helpers import *
|
from constants import *
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,10 @@ import sys
|
||||||
import os.path
|
import os.path
|
||||||
|
|
||||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
|
sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
|
||||||
from helpers import *
|
from constants import *
|
||||||
|
|
||||||
from mobject.tex_mobject import TexMobject
|
from mobject.tex_mobject import TexMobject
|
||||||
from mobject import Mobject, Group
|
from mobject.mobject import Mobject, Group
|
||||||
from mobject.image_mobject import ImageMobject
|
from mobject.image_mobject import ImageMobject
|
||||||
from mobject.vectorized_mobject import *
|
from mobject.vectorized_mobject import *
|
||||||
|
|
||||||
|
|
@ -29,10 +29,10 @@ from topics.three_dimensions import *
|
||||||
from topics.objects import *
|
from topics.objects import *
|
||||||
from topics.probability import *
|
from topics.probability import *
|
||||||
from topics.complex_numbers import *
|
from topics.complex_numbers import *
|
||||||
from scene import Scene
|
from scene.scene import Scene
|
||||||
from scene.reconfigurable_scene import ReconfigurableScene
|
from scene.reconfigurable_scene import ReconfigurableScene
|
||||||
from scene.zoomed_scene import *
|
from scene.zoomed_scene import *
|
||||||
from camera import Camera
|
from camera.camera import Camera
|
||||||
from mobject.svg_mobject import *
|
from mobject.svg_mobject import *
|
||||||
from mobject.tex_mobject import *
|
from mobject.tex_mobject import *
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ from animation import *
|
||||||
from mobject import *
|
from mobject import *
|
||||||
from constants import *
|
from constants import *
|
||||||
from mobject.region import *
|
from mobject.region import *
|
||||||
from scene import Scene, SceneFromVideo
|
from scene.scene import Scene, SceneFromVideo
|
||||||
from script_wrapper import command_line_create_scene
|
from script_wrapper import command_line_create_scene
|
||||||
|
|
||||||
MOVIE_PREFIX = "counting_in_binary/"
|
MOVIE_PREFIX = "counting_in_binary/"
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,9 @@ import itertools as it
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from helpers import *
|
from constants import *
|
||||||
|
|
||||||
from scene import Scene
|
from scene.scene import Scene
|
||||||
from geometry import Polygon
|
from geometry import Polygon
|
||||||
from mobject.region import region_from_polygon_vertices, region_from_line_boundary
|
from mobject.region import region_from_polygon_vertices, region_from_line_boundary
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import itertools as it
|
import itertools as it
|
||||||
|
|
||||||
from mobject import Mobject, Mobject1D, Mobject2D, Mobject
|
from mobject.mobject import Mobject, Mobject1D, Mobject2D, Mobject
|
||||||
from geometry import Line
|
from geometry import Line
|
||||||
from helpers import *
|
from constants import *
|
||||||
|
|
||||||
class Stars(Mobject1D):
|
class Stars(Mobject1D):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
import scipy
|
import scipy
|
||||||
from big_ol_pile_of_manim_imports import *
|
from big_ol_pile_of_manim_imports import *
|
||||||
|
from old_projects.fourier import *
|
||||||
|
|
||||||
FREQUENCY_COLOR = RED
|
FREQUENCY_COLOR = RED
|
||||||
USE_ALMOST_FOURIER_BY_DEFAULT = False
|
USE_ALMOST_FOURIER_BY_DEFAULT = False
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"scene"
|
"scene"
|
||||||
]
|
]
|
||||||
|
|
||||||
from scene import Scene
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
from helpers import *
|
from constants import *
|
||||||
|
|
||||||
from camera import MovingCamera
|
from camera.camera import MovingCamera
|
||||||
from scene import Scene
|
from .scene import Scene
|
||||||
from topics.geometry import ScreenRectangle
|
from topics.geometry import ScreenRectangle
|
||||||
|
|
||||||
class MovingCameraScene(Scene):
|
class MovingCameraScene(Scene):
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
from scene import Scene
|
from .scene import Scene
|
||||||
from animation.transform import Transform
|
from animation.transform import Transform
|
||||||
from mobject import Mobject
|
from mobject.mobject import Mobject
|
||||||
|
|
||||||
from helpers import *
|
from constants import *
|
||||||
|
|
||||||
class ReconfigurableScene(Scene):
|
class ReconfigurableScene(Scene):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
|
|
|
||||||
|
|
@ -10,16 +10,27 @@ import copy
|
||||||
from tqdm import tqdm as ProgressDisplay
|
from tqdm import tqdm as ProgressDisplay
|
||||||
import inspect
|
import inspect
|
||||||
import subprocess as sp
|
import subprocess as sp
|
||||||
|
import random
|
||||||
|
|
||||||
from helpers import *
|
from constants import *
|
||||||
|
|
||||||
from camera import Camera
|
from camera.camera import Camera
|
||||||
from tk_scene import TkSceneRoot
|
from tk_scene import TkSceneRoot
|
||||||
from mobject import Mobject, VMobject
|
from mobject.mobject import Mobject
|
||||||
from animation import Animation
|
from mobject.vectorized_mobject import VMobject
|
||||||
|
from animation.animation import Animation
|
||||||
from animation.transform import MoveToTarget
|
from animation.transform import MoveToTarget
|
||||||
from animation.continual_animation import ContinualAnimation
|
from animation.continual_animation import ContinualAnimation
|
||||||
from container import *
|
from utils.iterables import list_update
|
||||||
|
|
||||||
|
from container.container import Container
|
||||||
|
|
||||||
|
def add_extension_if_not_present(file_name, extension):
|
||||||
|
# This could conceivably be smarter about handling existing differing extensions
|
||||||
|
if(file_name[-len(extension):] != extension):
|
||||||
|
return file_name + extension
|
||||||
|
else:
|
||||||
|
return file_name
|
||||||
|
|
||||||
class Scene(Container):
|
class Scene(Container):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import cv2
|
||||||
import itertools as it
|
import itertools as it
|
||||||
from tqdm import tqdm as show_progress
|
from tqdm import tqdm as show_progress
|
||||||
|
|
||||||
from scene import Scene
|
from .scene import Scene
|
||||||
|
|
||||||
|
|
||||||
class SceneFromVideo(Scene):
|
class SceneFromVideo(Scene):
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,13 @@
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
from scene import Scene
|
from .scene import Scene
|
||||||
from animation.transform import FadeIn
|
from animation.transform import FadeIn
|
||||||
from mobject import Mobject
|
from mobject.mobject import Mobject
|
||||||
from topics.geometry import Rectangle
|
from topics.geometry import Rectangle
|
||||||
from camera import MovingCamera, Camera
|
from camera.camera import Camera
|
||||||
|
from camera.camera import MovingCamera
|
||||||
|
|
||||||
from helpers import *
|
from constants import *
|
||||||
|
|
||||||
class ZoomedScene(Scene):
|
class ZoomedScene(Scene):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import itertools as it
|
import itertools as it
|
||||||
|
|
||||||
from helpers import *
|
from constants import *
|
||||||
from scene import Scene
|
from scene.scene import Scene
|
||||||
from animation import Animation
|
from animation.animation import Animation
|
||||||
from mobject.tex_mobject import TexMobject
|
from mobject.tex_mobject import TexMobject
|
||||||
|
|
||||||
class RearrangeEquation(Scene):
|
class RearrangeEquation(Scene):
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
from helpers import *
|
import random
|
||||||
|
import numpy as np
|
||||||
|
import itertools as it
|
||||||
|
|
||||||
from mobject import Mobject
|
from constants import *
|
||||||
|
|
||||||
|
from mobject.mobject import Mobject, Group
|
||||||
from mobject.svg_mobject import SVGMobject
|
from mobject.svg_mobject import SVGMobject
|
||||||
from mobject.vectorized_mobject import VMobject, VGroup
|
from mobject.vectorized_mobject import VMobject, VGroup
|
||||||
from mobject.tex_mobject import TextMobject, TexMobject
|
from mobject.tex_mobject import TextMobject, TexMobject
|
||||||
|
|
@ -8,11 +12,14 @@ from mobject.tex_mobject import TextMobject, TexMobject
|
||||||
from topics.objects import Bubble, ThoughtBubble, SpeechBubble
|
from topics.objects import Bubble, ThoughtBubble, SpeechBubble
|
||||||
from topics.geometry import ScreenRectangle
|
from topics.geometry import ScreenRectangle
|
||||||
|
|
||||||
from animation import Animation
|
from animation.animation import Animation
|
||||||
from animation.transform import *
|
from animation.transform import Transform, ApplyMethod, MoveToTarget
|
||||||
|
from animation.transform import ReplacementTransform, FadeOut, FadeIn
|
||||||
from animation.simple_animations import Write, ShowCreation
|
from animation.simple_animations import Write, ShowCreation
|
||||||
from animation.compositions import AnimationGroup
|
from animation.compositions import AnimationGroup
|
||||||
from scene import Scene
|
from scene.scene import Scene
|
||||||
|
from utils.config_ops import digest_config
|
||||||
|
from utils.rate_functions import there_and_back, squish_rate_func
|
||||||
|
|
||||||
|
|
||||||
PI_CREATURE_DIR = os.path.join(MEDIA_DIR, "designs", "PiCreature")
|
PI_CREATURE_DIR = os.path.join(MEDIA_DIR, "designs", "PiCreature")
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
from helpers import *
|
from constants import *
|
||||||
|
|
||||||
from mobject.vectorized_mobject import VMobject
|
from mobject.vectorized_mobject import VMobject
|
||||||
from mobject.tex_mobject import TexMobject
|
from mobject.tex_mobject import TexMobject
|
||||||
|
|
||||||
from scene import Scene
|
from scene.scene import Scene
|
||||||
|
from utils.simple_functions import choose
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_COUNT_NUM_OFFSET = (FRAME_X_RADIUS - 1, FRAME_Y_RADIUS - 1, 0)
|
DEFAULT_COUNT_NUM_OFFSET = (FRAME_X_RADIUS - 1, FRAME_Y_RADIUS - 1, 0)
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
|
|
||||||
from helpers import *
|
from constants import *
|
||||||
|
|
||||||
from scene.scene import Scene
|
from scene.scene import Scene
|
||||||
from animation import Animation
|
from animation.animation import Animation
|
||||||
from animation.simple_animations import Write, DrawBorderThenFill
|
from animation.simple_animations import Write, DrawBorderThenFill
|
||||||
from animation.compositions import LaggedStart
|
from animation.compositions import LaggedStart
|
||||||
from animation.transform import FadeIn, FadeOut, ApplyMethod
|
from animation.transform import FadeIn, FadeOut, ApplyMethod
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,18 @@
|
||||||
from helpers import *
|
from constants import *
|
||||||
|
|
||||||
|
|
||||||
from mobject import VGroup
|
from mobject.vectorized_mobject import VGroup
|
||||||
from mobject.tex_mobject import TexMobject, TextMobject
|
from mobject.tex_mobject import TexMobject, TextMobject
|
||||||
from number_line import NumberPlane
|
from number_line import NumberPlane
|
||||||
from animation import Animation
|
from animation.animation import Animation
|
||||||
from animation.transform import ApplyPointwiseFunction, MoveToTarget
|
from animation.transform import ApplyPointwiseFunction, MoveToTarget
|
||||||
from animation.simple_animations import Homotopy, ShowCreation, \
|
from animation.simple_animations import Homotopy, ShowCreation, \
|
||||||
SmoothedVectorizedHomotopy
|
SmoothedVectorizedHomotopy
|
||||||
from scene import Scene
|
from scene.scene import Scene
|
||||||
|
from utils.config_ops import instantiate
|
||||||
|
from utils.config_ops import digest_config
|
||||||
|
from utils.paths import path_along_arc
|
||||||
|
from utils.space_ops import complex_to_R3, R3_to_complex
|
||||||
|
|
||||||
|
|
||||||
class ComplexTransformationScene(Scene):
|
class ComplexTransformationScene(Scene):
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
from helpers import *
|
from constants import *
|
||||||
|
|
||||||
from mobject.tex_mobject import TexMobject, TextMobject
|
from mobject.tex_mobject import TexMobject, TextMobject
|
||||||
from mobject import Mobject
|
from mobject.mobject import Mobject
|
||||||
from mobject.vectorized_mobject import VMobject, VGroup
|
from mobject.vectorized_mobject import VMobject, VGroup
|
||||||
|
|
||||||
from animation.transform import Transform, FadeIn, MoveToTarget
|
from animation.transform import Transform, FadeIn, MoveToTarget
|
||||||
from animation.simple_animations import ShowCreation
|
from animation.simple_animations import ShowCreation
|
||||||
from topics.geometry import Arrow, Circle, Dot
|
from topics.geometry import Arrow, Circle, Dot
|
||||||
# from topics.fractals import *
|
# from topics.fractals import *
|
||||||
from scene import Scene
|
from scene.scene import Scene
|
||||||
|
|
||||||
|
|
||||||
class CountingScene(Scene):
|
class CountingScene(Scene):
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,17 @@
|
||||||
# from mobject import Mobject, Point, Mobject1D
|
# from mobject.mobject import Mobject, Point, Mobject1D
|
||||||
from mobject.vectorized_mobject import VMobject, VGroup, VectorizedPoint
|
from mobject.vectorized_mobject import VMobject, VGroup, VectorizedPoint
|
||||||
from scene import Scene
|
from scene.scene import Scene
|
||||||
from animation.transform import Transform
|
from animation.transform import Transform
|
||||||
from animation.simple_animations import ShowCreation
|
from animation.simple_animations import ShowCreation
|
||||||
from topics.geometry import Line, Polygon, RegularPolygon, Square, Circle
|
from topics.geometry import Line, Polygon, RegularPolygon, Square, Circle
|
||||||
from characters import PiCreature, Randolph, get_all_pi_creature_modes
|
from characters import PiCreature, Randolph, get_all_pi_creature_modes
|
||||||
|
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 constants import *
|
||||||
|
|
||||||
from helpers import *
|
|
||||||
|
|
||||||
def rotate(points, angle = np.pi, axis = OUT):
|
def rotate(points, angle = np.pi, axis = OUT):
|
||||||
if axis is None:
|
if axis is None:
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
from scipy import integrate
|
from scipy import integrate
|
||||||
|
|
||||||
from mobject.vectorized_mobject import VMobject
|
from mobject.vectorized_mobject import VMobject
|
||||||
|
from utils.config_ops import digest_config
|
||||||
|
|
||||||
from helpers import *
|
from constants import *
|
||||||
|
|
||||||
class ParametricFunction(VMobject):
|
class ParametricFunction(VMobject):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,14 @@
|
||||||
from helpers import *
|
from constants import *
|
||||||
|
|
||||||
from mobject import Mobject
|
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 VMobject, VGroup
|
||||||
|
from utils.bezier import interpolate
|
||||||
|
from utils.config_ops import digest_config, digest_locals
|
||||||
|
from utils.paths import path_along_arc
|
||||||
|
from utils.space_ops import rotate_vector, angle_of_vector, compass_directions, center_of_mass
|
||||||
|
|
||||||
class Arc(VMobject):
|
class Arc(VMobject):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
from helpers import *
|
from constants import *
|
||||||
|
|
||||||
from scene import Scene
|
from scene.scene import Scene
|
||||||
# from topics.geometry import
|
# from topics.geometry import
|
||||||
from mobject.tex_mobject import TexMobject, TextMobject
|
from mobject.tex_mobject import TexMobject, TextMobject
|
||||||
from mobject.vectorized_mobject import VGroup, VectorizedPoint
|
from mobject.vectorized_mobject import VGroup, VectorizedPoint
|
||||||
|
|
@ -9,6 +9,10 @@ from animation.transform import Transform
|
||||||
from topics.number_line import NumberLine
|
from topics.number_line import NumberLine
|
||||||
from topics.functions import ParametricFunction
|
from topics.functions import ParametricFunction
|
||||||
from topics.geometry import Rectangle, DashedLine, Line
|
from topics.geometry import Rectangle, DashedLine, Line
|
||||||
|
from utils.bezier import interpolate
|
||||||
|
from utils.color import invert_color
|
||||||
|
from utils.color import color_gradient
|
||||||
|
from utils.space_ops import angle_of_vector
|
||||||
|
|
||||||
class GraphScene(Scene):
|
class GraphScene(Scene):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,11 @@ import numpy as np
|
||||||
import operator as op
|
import operator as op
|
||||||
from random import random
|
from random import random
|
||||||
|
|
||||||
from helpers import *
|
from constants import *
|
||||||
|
|
||||||
from scene import Scene
|
from scene.scene import Scene
|
||||||
|
from utils.rate_functions import there_and_back
|
||||||
|
from utils.space_ops import center_of_mass
|
||||||
|
|
||||||
|
|
||||||
class Graph():
|
class Graph():
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
from helpers import *
|
from constants import *
|
||||||
|
|
||||||
from mobject.tex_mobject import TexMobject
|
from mobject.tex_mobject import TexMobject
|
||||||
from mobject import Mobject
|
from mobject.mobject import Mobject
|
||||||
from mobject.vectorized_mobject import *
|
from mobject.vectorized_mobject import *
|
||||||
|
|
||||||
from animation.animation import Animation
|
from animation.animation import Animation
|
||||||
|
|
@ -13,14 +13,18 @@ from animation.continual_animation import *
|
||||||
from animation.playground import *
|
from animation.playground import *
|
||||||
from topics.geometry import *
|
from topics.geometry import *
|
||||||
from topics.functions import *
|
from topics.functions import *
|
||||||
from scene import Scene
|
from scene.scene import Scene
|
||||||
from camera import Camera
|
from camera.camera import Camera
|
||||||
from mobject.svg_mobject import *
|
from mobject.svg_mobject import *
|
||||||
from topics.three_dimensions import *
|
from topics.three_dimensions import *
|
||||||
|
|
||||||
from scipy.spatial import ConvexHull
|
from scipy.spatial import ConvexHull
|
||||||
from traceback import *
|
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
|
LIGHT_COLOR = YELLOW
|
||||||
SHADOW_COLOR = BLACK
|
SHADOW_COLOR = BLACK
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
from scene import Scene
|
from scene.scene import Scene
|
||||||
from mobject import Mobject
|
from mobject.mobject import Mobject
|
||||||
from mobject.vectorized_mobject import VMobject, VGroup
|
from mobject.vectorized_mobject import VMobject, VGroup
|
||||||
from mobject.tex_mobject import TexMobject, TextMobject
|
from mobject.tex_mobject import TexMobject, TextMobject
|
||||||
from animation.transform import ApplyPointwiseFunction, Transform, \
|
from animation.transform import ApplyPointwiseFunction, Transform, \
|
||||||
|
|
@ -10,7 +10,7 @@ from animation.simple_animations import ShowCreation, Write
|
||||||
from topics.number_line import NumberPlane, Axes
|
from topics.number_line import NumberPlane, Axes
|
||||||
from topics.geometry import Vector, Line, Circle, Arrow, Dot, BackgroundRectangle
|
from topics.geometry import Vector, Line, Circle, Arrow, Dot, BackgroundRectangle
|
||||||
|
|
||||||
from helpers import *
|
from constants import *
|
||||||
|
|
||||||
VECTOR_LABEL_SCALE_FACTOR = 0.8
|
VECTOR_LABEL_SCALE_FACTOR = 0.8
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
from helpers import *
|
from constants import *
|
||||||
|
|
||||||
from mobject import Mobject1D
|
|
||||||
from mobject.vectorized_mobject import VMobject, VGroup
|
from mobject.vectorized_mobject import VMobject, VGroup
|
||||||
from mobject.tex_mobject import TexMobject
|
from mobject.tex_mobject import TexMobject
|
||||||
from topics.geometry import Line, Arrow
|
from topics.geometry import Line, Arrow
|
||||||
from topics.functions import ParametricFunction
|
from topics.functions import ParametricFunction
|
||||||
from scene import Scene
|
from scene.scene import Scene
|
||||||
|
from utils.bezier import interpolate
|
||||||
|
from utils.config_ops import digest_config
|
||||||
|
from utils.space_ops import angle_of_vector
|
||||||
|
|
||||||
class NumberLine(VMobject):
|
class NumberLine(VMobject):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
|
|
||||||
from mobject.vectorized_mobject import VMobject, VGroup, VectorizedPoint
|
from mobject.vectorized_mobject import VMobject, VGroup, VectorizedPoint
|
||||||
from mobject.tex_mobject import TexMobject
|
from mobject.tex_mobject import TexMobject
|
||||||
from animation import Animation
|
from animation.animation import Animation
|
||||||
from animation.continual_animation import ContinualAnimation
|
from animation.continual_animation import ContinualAnimation
|
||||||
from topics.geometry import BackgroundRectangle
|
from topics.geometry import BackgroundRectangle
|
||||||
from scene import Scene
|
from scene.scene import Scene
|
||||||
from helpers import *
|
from constants import *
|
||||||
|
from utils.bezier import interpolate
|
||||||
|
from utils.config_ops import digest_config
|
||||||
|
|
||||||
class DecimalNumber(VMobject):
|
class DecimalNumber(VMobject):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
from helpers import *
|
from constants import *
|
||||||
|
|
||||||
from mobject import Mobject
|
from mobject.mobject import Mobject
|
||||||
from mobject.vectorized_mobject import VGroup, VMobject, VectorizedPoint
|
from mobject.vectorized_mobject import VGroup, VMobject, VectorizedPoint
|
||||||
from mobject.svg_mobject import SVGMobject
|
from mobject.svg_mobject import SVGMobject
|
||||||
from mobject.tex_mobject import TextMobject, TexMobject, Brace
|
from mobject.tex_mobject import TextMobject, TexMobject, Brace
|
||||||
|
|
||||||
from animation import Animation
|
from animation.animation import Animation
|
||||||
from animation.simple_animations import Rotating
|
from animation.simple_animations import Rotating
|
||||||
from animation.compositions import LaggedStart, AnimationGroup
|
from animation.compositions import LaggedStart, AnimationGroup
|
||||||
from animation.transform import ApplyMethod, FadeIn, GrowFromCenter
|
from animation.transform import ApplyMethod, FadeIn, GrowFromCenter
|
||||||
|
|
@ -13,6 +13,9 @@ from animation.transform import ApplyMethod, FadeIn, GrowFromCenter
|
||||||
from topics.geometry import Circle, Line, Rectangle, Square, \
|
from topics.geometry import Circle, Line, Rectangle, Square, \
|
||||||
Arc, Polygon, SurroundingRectangle
|
Arc, Polygon, SurroundingRectangle
|
||||||
from topics.three_dimensions import Cube
|
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
|
||||||
|
|
||||||
class Lightbulb(SVGMobject):
|
class Lightbulb(SVGMobject):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,23 @@
|
||||||
from helpers import *
|
from constants import *
|
||||||
|
|
||||||
from scene import Scene
|
from scene.scene import Scene
|
||||||
|
|
||||||
from animation.animation import Animation
|
from animation.animation import Animation
|
||||||
from animation.transform import Transform, MoveToTarget
|
from animation.transform import Transform, MoveToTarget
|
||||||
from animation.simple_animations import UpdateFromFunc
|
from animation.simple_animations import UpdateFromFunc
|
||||||
|
|
||||||
from mobject import Mobject
|
from mobject.mobject import Mobject
|
||||||
from mobject.vectorized_mobject import VGroup, VMobject, VectorizedPoint
|
from mobject.vectorized_mobject import VGroup, VMobject, VectorizedPoint
|
||||||
from mobject.svg_mobject import SVGMobject
|
from mobject.svg_mobject import SVGMobject
|
||||||
from mobject.tex_mobject import TextMobject, TexMobject, Brace
|
from mobject.tex_mobject import TextMobject, TexMobject, Brace
|
||||||
|
|
||||||
from topics.geometry import Circle, Line, Rectangle, Square, Arc, Polygon
|
from topics.geometry import Circle, Line, Rectangle, Square, Arc, Polygon
|
||||||
|
|
||||||
|
from utils.bezier import interpolate
|
||||||
|
from utils.color import color_gradient, average_color
|
||||||
|
from utils.config_ops import digest_config
|
||||||
|
from utils.iterables import tuplify
|
||||||
|
from utils.space_ops import center_of_mass
|
||||||
|
|
||||||
EPSILON = 0.0001
|
EPSILON = 0.0001
|
||||||
|
|
||||||
class SampleSpaceScene(Scene):
|
class SampleSpaceScene(Scene):
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,18 @@
|
||||||
|
|
||||||
from helpers import *
|
from constants import *
|
||||||
|
|
||||||
from mobject.vectorized_mobject import VGroup, VMobject, VectorizedPoint
|
from mobject.vectorized_mobject import VGroup, VMobject, VectorizedPoint
|
||||||
from topics.geometry import Square, Line
|
from topics.geometry import Square, Line
|
||||||
from scene import Scene
|
from scene.scene import Scene
|
||||||
from camera import Camera
|
from camera.camera import Camera
|
||||||
from animation.continual_animation import AmbientMovement
|
from animation.continual_animation import AmbientMovement
|
||||||
from animation.transform import ApplyMethod
|
from animation.transform import ApplyMethod
|
||||||
|
|
||||||
|
from utils.bezier import interpolate
|
||||||
|
from utils.iterables import list_update
|
||||||
|
from utils.space_ops import rotation_matrix, rotation_about_z, z_to_vector
|
||||||
|
|
||||||
|
|
||||||
class CameraWithPerspective(Camera):
|
class CameraWithPerspective(Camera):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
"camera_distance" : 20,
|
"camera_distance" : 20,
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
from scene import Scene
|
from scene.scene import Scene
|
||||||
from mobject import Mobject
|
from mobject.mobject import Mobject
|
||||||
from mobject.vectorized_mobject import VMobject, VGroup
|
from mobject.vectorized_mobject import VMobject, VGroup
|
||||||
from mobject.tex_mobject import TexMobject, TextMobject
|
from mobject.tex_mobject import TexMobject, TextMobject
|
||||||
from animation import Animation
|
from animation.animation import Animation
|
||||||
from animation.transform import ApplyPointwiseFunction, Transform, \
|
from animation.transform import ApplyPointwiseFunction, Transform, \
|
||||||
ApplyMethod, FadeOut, ApplyFunction
|
ApplyMethod, FadeOut, ApplyFunction
|
||||||
from animation.simple_animations import ShowCreation, Write
|
from animation.simple_animations import ShowCreation, Write
|
||||||
|
|
@ -12,8 +12,10 @@ from topics.number_line import NumberPlane, Axes
|
||||||
from topics.geometry import Vector, Line, Circle, Arrow, Dot, \
|
from topics.geometry import Vector, Line, Circle, Arrow, Dot, \
|
||||||
BackgroundRectangle, Square
|
BackgroundRectangle, Square
|
||||||
|
|
||||||
from helpers import *
|
from constants import *
|
||||||
from topics.matrix import Matrix, VECTOR_LABEL_SCALE_FACTOR, vector_coordinate_label
|
from topics.matrix import Matrix, VECTOR_LABEL_SCALE_FACTOR, vector_coordinate_label
|
||||||
|
from utils.rate_functions import rush_into, rush_from
|
||||||
|
from utils.space_ops import angle_of_vector
|
||||||
|
|
||||||
|
|
||||||
X_COLOR = GREEN_C
|
X_COLOR = GREEN_C
|
||||||
|
|
|
||||||
0
utils/__init__.py
Normal file
0
utils/__init__.py
Normal file
126
utils/bezier.py
Normal file
126
utils/bezier.py
Normal file
|
|
@ -0,0 +1,126 @@
|
||||||
|
import numpy as np
|
||||||
|
from scipy import linalg
|
||||||
|
from utils.simple_functions import choose
|
||||||
|
|
||||||
|
CLOSED_THRESHOLD = 0.0
|
||||||
|
|
||||||
|
def bezier(points):
|
||||||
|
n = len(points) - 1
|
||||||
|
return lambda t : sum([
|
||||||
|
((1-t)**(n-k))*(t**k)*choose(n, k)*point
|
||||||
|
for k, point in enumerate(points)
|
||||||
|
])
|
||||||
|
|
||||||
|
def partial_bezier_points(points, a, b):
|
||||||
|
"""
|
||||||
|
Given an array of points which define
|
||||||
|
a bezier curve, and two numbers 0<=a<b<=1,
|
||||||
|
return an array of the same size, which
|
||||||
|
describes the portion of the original bezier
|
||||||
|
curve on the interval [a, b].
|
||||||
|
|
||||||
|
This algorithm is pretty nifty, and pretty dense.
|
||||||
|
"""
|
||||||
|
a_to_1 = np.array([
|
||||||
|
bezier(points[i:])(a)
|
||||||
|
for i in range(len(points))
|
||||||
|
])
|
||||||
|
return np.array([
|
||||||
|
bezier(a_to_1[:i+1])((b-a)/(1.-a))
|
||||||
|
for i in range(len(points))
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
# Linear interpolation variants
|
||||||
|
|
||||||
|
def interpolate(start, end, alpha):
|
||||||
|
return (1-alpha)*start + alpha*end
|
||||||
|
|
||||||
|
def mid(start, end):
|
||||||
|
return (start + end)/2.0
|
||||||
|
|
||||||
|
def inverse_interpolate(start, end, value):
|
||||||
|
return np.true_divide(value - start, end - start)
|
||||||
|
|
||||||
|
def match_interpolate(new_start, new_end, old_start, old_end, old_value):
|
||||||
|
return interpolate(
|
||||||
|
new_start, new_end,
|
||||||
|
inverse_interpolate(old_start, old_end, old_value)
|
||||||
|
)
|
||||||
|
|
||||||
|
def clamp(lower, upper, val):
|
||||||
|
if val < lower:
|
||||||
|
return lower
|
||||||
|
elif val > upper:
|
||||||
|
return upper
|
||||||
|
return val
|
||||||
|
|
||||||
|
# Figuring out which bezier curves most smoothly connect a sequence of points
|
||||||
|
|
||||||
|
def get_smooth_handle_points(points):
|
||||||
|
points = np.array(points)
|
||||||
|
num_handles = len(points) - 1
|
||||||
|
dim = points.shape[1]
|
||||||
|
if num_handles < 1:
|
||||||
|
return np.zeros((0, dim)), np.zeros((0, dim))
|
||||||
|
#Must solve 2*num_handles equations to get the handles.
|
||||||
|
#l and u are the number of lower an upper diagonal rows
|
||||||
|
#in the matrix to solve.
|
||||||
|
l, u = 2, 1
|
||||||
|
#diag is a representation of the matrix in diagonal form
|
||||||
|
#See https://www.particleincell.com/2012/bezier-splines/
|
||||||
|
#for how to arive at these equations
|
||||||
|
diag = np.zeros((l+u+1, 2*num_handles))
|
||||||
|
diag[0,1::2] = -1
|
||||||
|
diag[0,2::2] = 1
|
||||||
|
diag[1,0::2] = 2
|
||||||
|
diag[1,1::2] = 1
|
||||||
|
diag[2,1:-2:2] = -2
|
||||||
|
diag[3,0:-3:2] = 1
|
||||||
|
#last
|
||||||
|
diag[2,-2] = -1
|
||||||
|
diag[1,-1] = 2
|
||||||
|
#This is the b as in Ax = b, where we are solving for x,
|
||||||
|
#and A is represented using diag. However, think of entries
|
||||||
|
#to x and b as being points in space, not numbers
|
||||||
|
b = np.zeros((2*num_handles, dim))
|
||||||
|
b[1::2] = 2*points[1:]
|
||||||
|
b[0] = points[0]
|
||||||
|
b[-1] = points[-1]
|
||||||
|
solve_func = lambda b : linalg.solve_banded(
|
||||||
|
(l, u), diag, b
|
||||||
|
)
|
||||||
|
if is_closed(points):
|
||||||
|
#Get equations to relate first and last points
|
||||||
|
matrix = diag_to_matrix((l, u), diag)
|
||||||
|
#last row handles second derivative
|
||||||
|
matrix[-1, [0, 1, -2, -1]] = [2, -1, 1, -2]
|
||||||
|
#first row handles first derivative
|
||||||
|
matrix[0,:] = np.zeros(matrix.shape[1])
|
||||||
|
matrix[0,[0, -1]] = [1, 1]
|
||||||
|
b[0] = 2*points[0]
|
||||||
|
b[-1] = np.zeros(dim)
|
||||||
|
solve_func = lambda b : linalg.solve(matrix, b)
|
||||||
|
handle_pairs = np.zeros((2*num_handles, dim))
|
||||||
|
for i in range(dim):
|
||||||
|
handle_pairs[:,i] = solve_func(b[:,i])
|
||||||
|
return handle_pairs[0::2], handle_pairs[1::2]
|
||||||
|
|
||||||
|
def diag_to_matrix(l_and_u, diag):
|
||||||
|
"""
|
||||||
|
Converts array whose rows represent diagonal
|
||||||
|
entries of a matrix into the matrix itself.
|
||||||
|
See scipy.linalg.solve_banded
|
||||||
|
"""
|
||||||
|
l, u = l_and_u
|
||||||
|
dim = diag.shape[1]
|
||||||
|
matrix = np.zeros((dim, dim))
|
||||||
|
for i in range(l+u+1):
|
||||||
|
np.fill_diagonal(
|
||||||
|
matrix[max(0,i-u):,max(0,u-i):],
|
||||||
|
diag[i,max(0,u-i):]
|
||||||
|
)
|
||||||
|
return matrix
|
||||||
|
|
||||||
|
def is_closed(points):
|
||||||
|
return np.linalg.norm(points[0] - points[-1]) < CLOSED_THRESHOLD
|
||||||
67
utils/color.py
Normal file
67
utils/color.py
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
from colour import Color
|
||||||
|
import numpy as np
|
||||||
|
import random
|
||||||
|
|
||||||
|
from utils.bezier import interpolate
|
||||||
|
|
||||||
|
def color_to_rgb(color):
|
||||||
|
return np.array(Color(color).get_rgb())
|
||||||
|
|
||||||
|
def color_to_rgba(color, alpha = 1):
|
||||||
|
return np.append(color_to_rgb(color), [alpha])
|
||||||
|
|
||||||
|
def rgb_to_color(rgb):
|
||||||
|
try:
|
||||||
|
return Color(rgb = rgb)
|
||||||
|
except:
|
||||||
|
return Color(WHITE)
|
||||||
|
|
||||||
|
def rgba_to_color(rgba):
|
||||||
|
return rgb_to_color(rgba[:3])
|
||||||
|
|
||||||
|
def rgb_to_hex(rgb):
|
||||||
|
return "#" + "".join('%02x'%int(255*x) for x in rgb)
|
||||||
|
|
||||||
|
def invert_color(color):
|
||||||
|
return rgb_to_color(1.0 - color_to_rgb(color))
|
||||||
|
|
||||||
|
def color_to_int_rgb(color):
|
||||||
|
return (255*color_to_rgb(color)).astype('uint8')
|
||||||
|
|
||||||
|
def color_to_int_rgba(color, alpha = 255):
|
||||||
|
return np.append(color_to_int_rgb(color), alpha)
|
||||||
|
|
||||||
|
def color_gradient(reference_colors, length_of_output):
|
||||||
|
if length_of_output == 0:
|
||||||
|
return reference_colors[0]
|
||||||
|
rgbs = map(color_to_rgb, reference_colors)
|
||||||
|
alphas = np.linspace(0, (len(rgbs) - 1), length_of_output)
|
||||||
|
floors = alphas.astype('int')
|
||||||
|
alphas_mod1 = alphas % 1
|
||||||
|
#End edge case
|
||||||
|
alphas_mod1[-1] = 1
|
||||||
|
floors[-1] = len(rgbs) - 2
|
||||||
|
return [
|
||||||
|
rgb_to_color(interpolate(rgbs[i], rgbs[i+1], alpha))
|
||||||
|
for i, alpha in zip(floors, alphas_mod1)
|
||||||
|
]
|
||||||
|
|
||||||
|
def interpolate_color(color1, color2, alpha):
|
||||||
|
rgb = interpolate(color_to_rgb(color1), color_to_rgb(color2), alpha)
|
||||||
|
return rgb_to_color(rgb)
|
||||||
|
|
||||||
|
def average_color(*colors):
|
||||||
|
rgbs = np.array(map(color_to_rgb, colors))
|
||||||
|
mean_rgb = np.apply_along_axis(np.mean, 0, rgbs)
|
||||||
|
return rgb_to_color(mean_rgb)
|
||||||
|
|
||||||
|
def random_bright_color():
|
||||||
|
color = random_color()
|
||||||
|
curr_rgb = color_to_rgb(color)
|
||||||
|
new_rgb = interpolate(
|
||||||
|
curr_rgb, np.ones(len(curr_rgb)), 0.5
|
||||||
|
)
|
||||||
|
return Color(rgb = new_rgb)
|
||||||
|
|
||||||
|
def random_color():
|
||||||
|
return random.choice(PALETTE)
|
||||||
115
utils/config_ops.py
Normal file
115
utils/config_ops.py
Normal file
|
|
@ -0,0 +1,115 @@
|
||||||
|
import inspect
|
||||||
|
import operator as op
|
||||||
|
|
||||||
|
def instantiate(obj):
|
||||||
|
"""
|
||||||
|
Useful so that classes or instance of those classes can be
|
||||||
|
included in configuration, which can prevent defaults from
|
||||||
|
getting created during compilation/importing
|
||||||
|
"""
|
||||||
|
return obj() if isinstance(obj, type) else obj
|
||||||
|
|
||||||
|
def get_all_descendent_classes(Class):
|
||||||
|
awaiting_review = [Class]
|
||||||
|
result = []
|
||||||
|
while awaiting_review:
|
||||||
|
Child = awaiting_review.pop()
|
||||||
|
awaiting_review += Child.__subclasses__()
|
||||||
|
result.append(Child)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def filtered_locals(caller_locals):
|
||||||
|
result = caller_locals.copy()
|
||||||
|
ignored_local_args = ["self", "kwargs"]
|
||||||
|
for arg in ignored_local_args:
|
||||||
|
result.pop(arg, caller_locals)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def digest_config(obj, kwargs, caller_locals = {}):
|
||||||
|
"""
|
||||||
|
Sets init args and CONFIG values as local variables
|
||||||
|
|
||||||
|
The purpose of this function is to ensure that all
|
||||||
|
configuration of any object is inheritable, able to
|
||||||
|
be easily passed into instantiation, and is attached
|
||||||
|
as an attribute of the object.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Assemble list of CONFIGs from all super classes
|
||||||
|
classes_in_hierarchy = [obj.__class__]
|
||||||
|
static_configs = []
|
||||||
|
while len(classes_in_hierarchy) > 0:
|
||||||
|
Class = classes_in_hierarchy.pop()
|
||||||
|
classes_in_hierarchy += Class.__bases__
|
||||||
|
if hasattr(Class, "CONFIG"):
|
||||||
|
static_configs.append(Class.CONFIG)
|
||||||
|
|
||||||
|
#Order matters a lot here, first dicts have higher priority
|
||||||
|
caller_locals = filtered_locals(caller_locals)
|
||||||
|
all_dicts = [kwargs, caller_locals, obj.__dict__]
|
||||||
|
all_dicts += static_configs
|
||||||
|
all_new_dicts = [kwargs, caller_locals] + static_configs
|
||||||
|
obj.__dict__ = merge_config(all_dicts)
|
||||||
|
#Keep track of the configuration of objects upon
|
||||||
|
#instantiation
|
||||||
|
obj.initial_config = merge_config(all_new_dicts)
|
||||||
|
|
||||||
|
def merge_config(all_dicts):
|
||||||
|
all_config = reduce(op.add, [d.items() for d in all_dicts])
|
||||||
|
config = dict()
|
||||||
|
for c in all_config:
|
||||||
|
key, value = c
|
||||||
|
if not key in config:
|
||||||
|
config[key] = value
|
||||||
|
else:
|
||||||
|
#When two dictionaries have the same key, they are merged.
|
||||||
|
if isinstance(value, dict) and isinstance(config[key], dict):
|
||||||
|
config[key] = merge_config([config[key], value])
|
||||||
|
return config
|
||||||
|
|
||||||
|
def soft_dict_update(d1, d2):
|
||||||
|
"""
|
||||||
|
Adds key values pairs of d2 to d1 only when d1 doesn't
|
||||||
|
already have that key
|
||||||
|
"""
|
||||||
|
for key, value in d2.items():
|
||||||
|
if key not in d1:
|
||||||
|
d1[key] = value
|
||||||
|
|
||||||
|
def digest_locals(obj, keys = None):
|
||||||
|
caller_locals = filtered_locals(
|
||||||
|
inspect.currentframe().f_back.f_locals
|
||||||
|
)
|
||||||
|
if keys is None:
|
||||||
|
keys = caller_locals.keys()
|
||||||
|
for key in keys:
|
||||||
|
setattr(obj, key, caller_locals[key])
|
||||||
|
|
||||||
|
# Occasionally convenient in order to write dict.x instead of more laborious
|
||||||
|
# (and less in keeping with all other attr accesses) dict["x"]
|
||||||
|
class DictAsObject(object):
|
||||||
|
def __init__(self, dict):
|
||||||
|
self.__dict__ = dict
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
30
utils/images.py
Normal file
30
utils/images.py
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
import numpy as np
|
||||||
|
from PIL import Image
|
||||||
|
from constants import RASTER_IMAGE_DIR
|
||||||
|
import os
|
||||||
|
|
||||||
|
def get_full_raster_image_path(image_file_name):
|
||||||
|
possible_paths = [
|
||||||
|
image_file_name,
|
||||||
|
os.path.join(RASTER_IMAGE_DIR, image_file_name),
|
||||||
|
os.path.join(RASTER_IMAGE_DIR, image_file_name + ".jpg"),
|
||||||
|
os.path.join(RASTER_IMAGE_DIR, image_file_name + ".png"),
|
||||||
|
os.path.join(RASTER_IMAGE_DIR, image_file_name + ".gif"),
|
||||||
|
]
|
||||||
|
for path in possible_paths:
|
||||||
|
if os.path.exists(path):
|
||||||
|
return path
|
||||||
|
raise IOError("File %s not Found"%image_file_name)
|
||||||
|
|
||||||
|
def drag_pixels(frames):
|
||||||
|
curr = frames[0]
|
||||||
|
new_frames = []
|
||||||
|
for frame in frames:
|
||||||
|
curr += (curr == 0) * np.array(frame)
|
||||||
|
new_frames.append(np.array(curr))
|
||||||
|
return new_frames
|
||||||
|
|
||||||
|
def invert_image(image):
|
||||||
|
arr = np.array(image)
|
||||||
|
arr = (255 * np.ones(arr.shape)).astype(arr.dtype) - arr
|
||||||
|
return Image.fromarray(arr)
|
||||||
113
utils/iterables.py
Normal file
113
utils/iterables.py
Normal file
|
|
@ -0,0 +1,113 @@
|
||||||
|
import numpy as np
|
||||||
|
import itertools as it
|
||||||
|
|
||||||
|
def remove_list_redundancies(l):
|
||||||
|
"""
|
||||||
|
Used instead of list(set(l)) to maintain order
|
||||||
|
Keeps the last occurance of each element
|
||||||
|
"""
|
||||||
|
reversed_result = []
|
||||||
|
used = set()
|
||||||
|
for x in reversed(l):
|
||||||
|
if not x in used:
|
||||||
|
reversed_result.append(x)
|
||||||
|
used.add(x)
|
||||||
|
reversed_result.reverse()
|
||||||
|
return reversed_result
|
||||||
|
|
||||||
|
def list_update(l1, l2):
|
||||||
|
"""
|
||||||
|
Used instead of list(set(l1).update(l2)) to maintain order,
|
||||||
|
making sure duplicates are removed from l1, not l2.
|
||||||
|
"""
|
||||||
|
return filter(lambda e : e not in l2, l1) + list(l2)
|
||||||
|
|
||||||
|
def list_difference_update(l1, l2):
|
||||||
|
return filter(lambda e : e not in l2, l1)
|
||||||
|
|
||||||
|
def all_elements_are_instances(iterable, Class):
|
||||||
|
return all(map(lambda e : isinstance(e, Class), iterable))
|
||||||
|
|
||||||
|
def adjacent_pairs(objects):
|
||||||
|
return zip(objects, list(objects[1:])+[objects[0]])
|
||||||
|
|
||||||
|
def batch_by_property(items, property_func):
|
||||||
|
"""
|
||||||
|
Takes in a list, and returns a list of tuples, (batch, prop)
|
||||||
|
such that all items in a batch have the same output when
|
||||||
|
put into property_func, and such that chaining all these
|
||||||
|
batches together would give the original list.
|
||||||
|
"""
|
||||||
|
batch_prop_pairs = []
|
||||||
|
def add_batch_prop_pair(batch):
|
||||||
|
if len(batch) > 0:
|
||||||
|
batch_prop_pairs.append(
|
||||||
|
(batch, property_func(batch[0]))
|
||||||
|
)
|
||||||
|
curr_batch = []
|
||||||
|
curr_prop = None
|
||||||
|
for item in items:
|
||||||
|
prop = property_func(item)
|
||||||
|
if prop != curr_prop:
|
||||||
|
add_batch_prop_pair(curr_batch)
|
||||||
|
curr_prop = prop
|
||||||
|
curr_batch = [item]
|
||||||
|
else:
|
||||||
|
curr_batch.append(item)
|
||||||
|
add_batch_prop_pair(curr_batch)
|
||||||
|
return batch_prop_pairs
|
||||||
|
|
||||||
|
def tuplify(obj):
|
||||||
|
if isinstance(obj, str):
|
||||||
|
return (obj,)
|
||||||
|
try:
|
||||||
|
return tuple(obj)
|
||||||
|
except:
|
||||||
|
return (obj,)
|
||||||
|
|
||||||
|
def stretch_array_to_length(nparray, length):
|
||||||
|
curr_len = len(nparray)
|
||||||
|
if curr_len > length:
|
||||||
|
raise Warning("Trying to stretch array to a length shorter than its own")
|
||||||
|
indices = np.arange(length)/ float(length)
|
||||||
|
indices *= curr_len
|
||||||
|
return nparray[indices.astype('int')]
|
||||||
|
|
||||||
|
def make_even(iterable_1, iterable_2):
|
||||||
|
list_1, list_2 = list(iterable_1), list(iterable_2)
|
||||||
|
length = max(len(list_1), len(list_2))
|
||||||
|
return (
|
||||||
|
[list_1[(n * len(list_1)) / length] for n in xrange(length)],
|
||||||
|
[list_2[(n * len(list_2)) / length] for n in xrange(length)]
|
||||||
|
)
|
||||||
|
|
||||||
|
def make_even_by_cycling(iterable_1, iterable_2):
|
||||||
|
length = max(len(iterable_1), len(iterable_2))
|
||||||
|
cycle1 = it.cycle(iterable_1)
|
||||||
|
cycle2 = it.cycle(iterable_2)
|
||||||
|
return (
|
||||||
|
[cycle1.next() for x in range(length)],
|
||||||
|
[cycle2.next() for x in range(length)]
|
||||||
|
)
|
||||||
|
|
||||||
|
def composition(func_list):
|
||||||
|
"""
|
||||||
|
func_list should contain elements of the form (f, args)
|
||||||
|
"""
|
||||||
|
return reduce(
|
||||||
|
lambda (f1, args1), (f2, args2) : (lambda x : f1(f2(x, *args2), *args1)),
|
||||||
|
func_list,
|
||||||
|
lambda x : x
|
||||||
|
)
|
||||||
|
|
||||||
|
def remove_nones(sequence):
|
||||||
|
return filter(lambda x : x, sequence)
|
||||||
|
|
||||||
|
## Note this is redundant with it.chain
|
||||||
|
def concatenate_lists(*list_of_lists):
|
||||||
|
return [item for l in list_of_lists for item in l]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
40
utils/paths.py
Normal file
40
utils/paths.py
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
import numpy as np
|
||||||
|
from utils.bezier import interpolate
|
||||||
|
from utils.space_ops import rotation_matrix
|
||||||
|
from constants import OUT
|
||||||
|
|
||||||
|
STRAIGHT_PATH_THRESHOLD = 0.01
|
||||||
|
|
||||||
|
def straight_path(start_points, end_points, alpha):
|
||||||
|
"""
|
||||||
|
Same function as interpolate, but renamed to reflect
|
||||||
|
intent of being used to determine how a set of points move
|
||||||
|
to another set. For instance, it should be a specific case
|
||||||
|
of path_along_arc
|
||||||
|
"""
|
||||||
|
return interpolate(start_points, end_points, alpha)
|
||||||
|
|
||||||
|
def path_along_arc(arc_angle, axis = OUT):
|
||||||
|
"""
|
||||||
|
If vect is vector from start to end, [vect[:,1], -vect[:,0]] is
|
||||||
|
perpendicular to vect in the left direction.
|
||||||
|
"""
|
||||||
|
if abs(arc_angle) < STRAIGHT_PATH_THRESHOLD:
|
||||||
|
return straight_path
|
||||||
|
if np.linalg.norm(axis) == 0:
|
||||||
|
axis = OUT
|
||||||
|
unit_axis = axis/np.linalg.norm(axis)
|
||||||
|
def path(start_points, end_points, alpha):
|
||||||
|
vects = end_points - start_points
|
||||||
|
centers = start_points + 0.5*vects
|
||||||
|
if arc_angle != np.pi:
|
||||||
|
centers += np.cross(unit_axis, vects/2.0)/np.tan(arc_angle/2)
|
||||||
|
rot_matrix = rotation_matrix(alpha*arc_angle, unit_axis)
|
||||||
|
return centers + np.dot(start_points-centers, rot_matrix.T)
|
||||||
|
return path
|
||||||
|
|
||||||
|
def clockwise_path():
|
||||||
|
return path_along_arc(-np.pi)
|
||||||
|
|
||||||
|
def counterclockwise_path():
|
||||||
|
return path_along_arc(np.pi)
|
||||||
68
utils/rate_functions.py
Normal file
68
utils/rate_functions.py
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
import numpy as np
|
||||||
|
from utils.simple_functions import sigmoid
|
||||||
|
from utils.bezier import bezier
|
||||||
|
|
||||||
|
def smooth(t, inflection = 10.0):
|
||||||
|
error = sigmoid(-inflection / 2)
|
||||||
|
return (sigmoid(inflection*(t - 0.5)) - error) / (1 - 2*error)
|
||||||
|
|
||||||
|
def rush_into(t):
|
||||||
|
return 2*smooth(t/2.0)
|
||||||
|
|
||||||
|
def rush_from(t):
|
||||||
|
return 2*smooth(t/2.0+0.5) - 1
|
||||||
|
|
||||||
|
def slow_into(t):
|
||||||
|
return np.sqrt(1-(1-t)*(1-t))
|
||||||
|
|
||||||
|
def double_smooth(t):
|
||||||
|
if t < 0.5:
|
||||||
|
return 0.5*smooth(2*t)
|
||||||
|
else:
|
||||||
|
return 0.5*(1 + smooth(2*t - 1))
|
||||||
|
|
||||||
|
def there_and_back(t, inflection = 10.0):
|
||||||
|
new_t = 2*t if t < 0.5 else 2*(1 - t)
|
||||||
|
return smooth(new_t, inflection)
|
||||||
|
|
||||||
|
def there_and_back_with_pause(t):
|
||||||
|
if t < 1./3:
|
||||||
|
return smooth(3*t)
|
||||||
|
elif t < 2./3:
|
||||||
|
return 1
|
||||||
|
else:
|
||||||
|
return smooth(3 - 3*t)
|
||||||
|
|
||||||
|
def running_start(t, pull_factor = -0.5):
|
||||||
|
return bezier([0, 0, pull_factor, pull_factor, 1, 1, 1])(t)
|
||||||
|
|
||||||
|
def not_quite_there(func = smooth, proportion = 0.7):
|
||||||
|
def result(t):
|
||||||
|
return proportion*func(t)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def wiggle(t, wiggles = 2):
|
||||||
|
return there_and_back(t) * np.sin(wiggles*np.pi*t)
|
||||||
|
|
||||||
|
def squish_rate_func(func, a = 0.4, b = 0.6):
|
||||||
|
def result(t):
|
||||||
|
if a == b:
|
||||||
|
return a
|
||||||
|
|
||||||
|
if t < a:
|
||||||
|
return func(0)
|
||||||
|
elif t > b:
|
||||||
|
return func(1)
|
||||||
|
else:
|
||||||
|
return func((t-a)/(b-a))
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
# Stylistically, should this take parameters (with default values)?
|
||||||
|
# Ultimately, the functionality is entirely subsumed by squish_rate_func,
|
||||||
|
# but it may be useful to have a nice name for with nice default params for
|
||||||
|
# "lingering", different from squish_rate_func's default params
|
||||||
|
def lingering(t):
|
||||||
|
return squish_rate_func(lambda t: t, 0, 0.8)(t)
|
||||||
|
|
||||||
|
|
||||||
27
utils/simple_functions.py
Normal file
27
utils/simple_functions.py
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
import operator as op
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
def sigmoid(x):
|
||||||
|
return 1.0/(1 + np.exp(-x))
|
||||||
|
|
||||||
|
def choose(n, r):
|
||||||
|
if n < r: return 0
|
||||||
|
if r == 0: return 1
|
||||||
|
denom = reduce(op.mul, xrange(1, r+1), 1)
|
||||||
|
numer = reduce(op.mul, xrange(n, n-r, -1), 1)
|
||||||
|
return numer//denom
|
||||||
|
|
||||||
|
# Just to have a less heavyweight name for this extremely common operation
|
||||||
|
#
|
||||||
|
# We may wish to have more fine-grained control over division by zero behavior
|
||||||
|
# in the future (separate specifiable values for 0/0 and x/0 with x != 0),
|
||||||
|
# but for now, we just allow the option to handle indeterminate 0/0.
|
||||||
|
def fdiv(a, b, zero_over_zero_value = None):
|
||||||
|
if zero_over_zero_value != None:
|
||||||
|
out = np.full_like(a, zero_over_zero_value)
|
||||||
|
where = np.logical_or (a != 0, b != 0)
|
||||||
|
else:
|
||||||
|
out = None
|
||||||
|
where = True
|
||||||
|
|
||||||
|
return np.true_divide(a, b, out = out, where = where)
|
||||||
26
utils/sounds.py
Normal file
26
utils/sounds.py
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
import os
|
||||||
|
|
||||||
|
def play_chord(*nums):
|
||||||
|
commands = [
|
||||||
|
"play",
|
||||||
|
"-n",
|
||||||
|
"-c1",
|
||||||
|
"--no-show-progress",
|
||||||
|
"synth",
|
||||||
|
] + [
|
||||||
|
"sin %-"+str(num)
|
||||||
|
for num in nums
|
||||||
|
] + [
|
||||||
|
"fade h 0.5 1 0.5",
|
||||||
|
"> /dev/null"
|
||||||
|
]
|
||||||
|
try:
|
||||||
|
os.system(" ".join(commands))
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def play_error_sound():
|
||||||
|
play_chord(11, 8, 6, 1)
|
||||||
|
|
||||||
|
def play_finish_sound():
|
||||||
|
play_chord(12, 9, 5, 2)
|
||||||
110
utils/space_ops.py
Normal file
110
utils/space_ops.py
Normal file
|
|
@ -0,0 +1,110 @@
|
||||||
|
import numpy as np
|
||||||
|
from constants import RIGHT, OUT
|
||||||
|
|
||||||
|
#Matrix operations
|
||||||
|
|
||||||
|
def thick_diagonal(dim, thickness = 2):
|
||||||
|
row_indices = np.arange(dim).repeat(dim).reshape((dim, dim))
|
||||||
|
col_indices = np.transpose(row_indices)
|
||||||
|
return (np.abs(row_indices - col_indices)<thickness).astype('uint8')
|
||||||
|
|
||||||
|
def rotation_matrix(angle, axis):
|
||||||
|
"""
|
||||||
|
Rotation in R^3 about a specified axis of rotation.
|
||||||
|
"""
|
||||||
|
about_z = rotation_about_z(angle)
|
||||||
|
z_to_axis = z_to_vector(axis)
|
||||||
|
axis_to_z = np.linalg.inv(z_to_axis)
|
||||||
|
return reduce(np.dot, [z_to_axis, about_z, axis_to_z])
|
||||||
|
|
||||||
|
def rotation_about_z(angle):
|
||||||
|
return [
|
||||||
|
[np.cos(angle), -np.sin(angle), 0],
|
||||||
|
[np.sin(angle), np.cos(angle), 0],
|
||||||
|
[0, 0, 1]
|
||||||
|
]
|
||||||
|
|
||||||
|
def z_to_vector(vector):
|
||||||
|
"""
|
||||||
|
Returns some matrix in SO(3) which takes the z-axis to the
|
||||||
|
(normalized) vector provided as an argument
|
||||||
|
"""
|
||||||
|
norm = np.linalg.norm(vector)
|
||||||
|
if norm == 0:
|
||||||
|
return np.identity(3)
|
||||||
|
v = np.array(vector) / norm
|
||||||
|
phi = np.arccos(v[2])
|
||||||
|
if any(v[:2]):
|
||||||
|
#projection of vector to unit circle
|
||||||
|
axis_proj = v[:2] / np.linalg.norm(v[:2])
|
||||||
|
theta = np.arccos(axis_proj[0])
|
||||||
|
if axis_proj[1] < 0:
|
||||||
|
theta = -theta
|
||||||
|
else:
|
||||||
|
theta = 0
|
||||||
|
phi_down = np.array([
|
||||||
|
[np.cos(phi), 0, np.sin(phi)],
|
||||||
|
[0, 1, 0],
|
||||||
|
[-np.sin(phi), 0, np.cos(phi)]
|
||||||
|
])
|
||||||
|
return np.dot(rotation_about_z(theta), phi_down)
|
||||||
|
|
||||||
|
def rotate_vector(vector, angle, axis = OUT):
|
||||||
|
return np.dot(rotation_matrix(angle, axis), vector)
|
||||||
|
|
||||||
|
def angle_between(v1, v2):
|
||||||
|
return np.arccos(np.dot(
|
||||||
|
v1 / np.linalg.norm(v1),
|
||||||
|
v2 / np.linalg.norm(v2)
|
||||||
|
))
|
||||||
|
|
||||||
|
def angle_of_vector(vector):
|
||||||
|
"""
|
||||||
|
Returns polar coordinate theta when vector is project on xy plane
|
||||||
|
"""
|
||||||
|
z = complex(*vector[:2])
|
||||||
|
if z == 0:
|
||||||
|
return 0
|
||||||
|
return np.angle(complex(*vector[:2]))
|
||||||
|
|
||||||
|
def angle_between_vectors(v1, v2):
|
||||||
|
"""
|
||||||
|
Returns the angle between two 3D vectors.
|
||||||
|
This angle will always be btw 0 and TAU/2.
|
||||||
|
"""
|
||||||
|
l1 = np.linalg.norm(v1)
|
||||||
|
l2 = np.linalg.norm(v2)
|
||||||
|
return np.arccos(np.dot(v1,v2)/(l1*l2))
|
||||||
|
|
||||||
|
def project_along_vector(point, vector):
|
||||||
|
matrix = np.identity(3) - np.outer(vector, vector)
|
||||||
|
return np.dot(point, matrix.T)
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
def compass_directions(n = 4, start_vect = RIGHT):
|
||||||
|
angle = 2*np.pi/n
|
||||||
|
return np.array([
|
||||||
|
rotate_vector(start_vect, k*angle)
|
||||||
|
for k in range(n)
|
||||||
|
])
|
||||||
|
|
||||||
|
def complex_to_R3(complex_num):
|
||||||
|
return np.array((complex_num.real, complex_num.imag, 0))
|
||||||
|
|
||||||
|
def R3_to_complex(point):
|
||||||
|
return complex(*point[:2])
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
19
utils/strings.py
Normal file
19
utils/strings.py
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
import re
|
||||||
|
import string
|
||||||
|
|
||||||
|
def to_camel_case(name):
|
||||||
|
return "".join([
|
||||||
|
filter(
|
||||||
|
lambda c : c not in string.punctuation + string.whitespace, part
|
||||||
|
).capitalize()
|
||||||
|
for part in name.split("_")
|
||||||
|
])
|
||||||
|
|
||||||
|
def initials(name, sep_values = [" ", "_"]):
|
||||||
|
return "".join([
|
||||||
|
(s[0] if s else "")
|
||||||
|
for s in re.split("|".join(sep_values), name)
|
||||||
|
])
|
||||||
|
|
||||||
|
def camel_case_initials(name):
|
||||||
|
return filter(lambda c : c.isupper(), name)
|
||||||
Loading…
Add table
Reference in a new issue