diff --git a/__init__.py b/__init__.py index 33c2412e..8b137891 100644 --- a/__init__.py +++ b/__init__.py @@ -1,13 +1 @@ -from animation import * -from scene import * -from topics import * -from constants import * -from displayer import * -from extract_scene import * -from helpers import * -from image_mobject import * -from mobject import * -from old_proje import * -from playground import * -from region import * -from tex_utils import * + diff --git a/animation/__init__.py b/animation/__init__.py index fc153ea0..0d0d6b11 100644 --- a/animation/__init__.py +++ b/animation/__init__.py @@ -1,4 +1,8 @@ -from animation import * -from meta_animations import * -from simple_animations import * -from transform import * +__all__ = [ + "animation", + "meta_animations", + "simple_animations", + "transform" +] + +from animation import Animation diff --git a/animation/animation.py b/animation/animation.py index 75732e91..5b001982 100644 --- a/animation/animation.py +++ b/animation/animation.py @@ -8,7 +8,7 @@ import progressbar import inspect from helpers import * -from mobject import Mobject, Point +from mobject import Mobject class Animation(object): DEFAULT_CONFIG = { diff --git a/animation/transform.py b/animation/transform.py index 5a977437..912b91c8 100644 --- a/animation/transform.py +++ b/animation/transform.py @@ -7,7 +7,8 @@ import warnings from helpers import * from animation import Animation -from mobject import Mobject, Point +from mobject import Mobject +from topics.geometry import Point class Transform(Animation): DEFAULT_CONFIG = { diff --git a/constants.py b/constants.py index 287e5e7f..c8fce368 100644 --- a/constants.py +++ b/constants.py @@ -30,7 +30,7 @@ DEFAULT_POINT_DENSITY_1D = 200 DEFAULT_POINT_THICKNESS = 6 -#TODO, Make sure these are not needd +#TODO, Make sure these are not needed SPACE_HEIGHT = 4.0 SPACE_WIDTH = SPACE_HEIGHT * DEFAULT_WIDTH / DEFAULT_HEIGHT diff --git a/extract_scene.py b/extract_scene.py index 5f8e98db..08483473 100644 --- a/extract_scene.py +++ b/extract_scene.py @@ -137,6 +137,9 @@ def prompt_user_for_choice(name_to_obj): sys.exit() def get_scene_args(SceneClass, config): + """ + Return arguments as a sequence + """ tuplify = lambda x : x if type(x) == tuple else (x,) args_list = map(tuplify, SceneClass.args_list) preset_extensions = [ @@ -192,7 +195,7 @@ def main(): scene_kwargs["announce_construction"] = True for SceneClass in get_scene_classes(scene_names_to_classes, config): for args in get_scene_args(SceneClass, config): - scene_kwargs["construct_args"] = args + scene_kwargs["construct_args"] = tuplify(args) try: handle_scene(SceneClass(**scene_kwargs), **config) except: diff --git a/helpers.py b/helpers.py index 60bee399..7e3c4e8f 100644 --- a/helpers.py +++ b/helpers.py @@ -3,7 +3,7 @@ import itertools as it import operator as op from PIL import Image from colour import Color -from random import random +import random import inspect import string import re @@ -11,6 +11,14 @@ import re from constants import * + +def bezier(points): + n = len(points) - 1 + return lambda t : sum([ + ((1-t)**(n-k))*(t**k)*choose(n, k)*point + for point, k in zip(points, it.count()) + ]) + def remove_list_redundancies(l): """ Used instead of lsit(set(l)) to maintain order @@ -33,6 +41,12 @@ def adjascent_pairs(objects): def complex_to_R3(complex_num): return np.array((complex_num.real, complex_num.imag, 0)) +def tuplify(obj): + try: + return tuple(obj) + except: + return (obj,) + def instantiate(obj): """ Useful so that classes or instance of those classes can be @@ -120,9 +134,7 @@ def intersection(line1, line2): return result def random_color(): - color = Color() - color.set_rgb([1 - 0.5 * random() for x in range(3)]) - return color + return random.choice(PALETTE) ################################################ @@ -314,7 +326,7 @@ def z_to_vector(vector): ]) return np.dot(rotation_about_z(theta), phi_down) -def rotate_vector(vector, angle, axis): +def rotate_vector(vector, angle, axis = OUT): return np.dot(rotation_matrix(angle, axis), vector) def angle_between(v1, v2): diff --git a/hilbert.py b/hilbert.py new file mode 100644 index 00000000..e05ef15f --- /dev/null +++ b/hilbert.py @@ -0,0 +1,155 @@ +from mobject import Mobject, Mobject1D +from scene import Scene +from animation.transform import Transform +from animation.simple_animations import ShowCreation +from topics.geometry import Line, Point + +from helpers import * + +def flip_over_slope_1(points): + return points[:,[1, 0 , 2]] + +def flip_over_slope_neg_1(points): + return -points[:,[1, 0, 2]] + +def hilbertification(points, radius=3): + transformed_copies = [ + flip(points/2) + offset*radius/2.0 + for flip, offset in [ + (flip_over_slope_1, (LEFT+DOWN)), + (lambda x : x, (LEFT+UP)), + (lambda x : x, (RIGHT+UP)), + (flip_over_slope_neg_1, (RIGHT+DOWN)), + ] + ] + return reduce( + lambda a, b : np.append(a, b, axis = 0), + transformed_copies + ) + + + +class SpaceFillingCurve(Mobject1D): + DEFAULT_CONFIG = { + "radius" : 3, + "order" : 5, + "start_color" : RED, + "end_color" : GREEN, + } + + def generate_points(self): + points = self.get_anchor_points(self.order) + for pair in zip(points, points[1:]): + self.add_line(*pair, min_density = 0.01) + self.gradient_highlight(self.start_color, self.end_color) + + def get_anchor_points(self, order): + """ + To be filled out in subclasses + """ + return [] + + +class HilbertCurve(SpaceFillingCurve): + def get_anchor_points(self, order): + points = np.zeros((1, 3)) + for count in range(order): + points = hilbertification(points) + return points + +class HilbertCurve3D(SpaceFillingCurve): + def get_anchor_points(self, order): + pass + +class SnakeCurve(SpaceFillingCurve): + DEFAULT_CONFIG = { + "start_color" : BLUE, + "end_color" : YELLOW, + } + def get_anchor_points(self, order): + result = [] + lower_left = ORIGIN + \ + LEFT*self.radius + \ + DOWN*self.radius + step = 2.0*self.radius / (order) + for y in range(order+1): + x_range = range(order+1) + if y%2 == 0: + x_range.reverse() + for x in x_range: + result.append( + lower_left + x*step*RIGHT + y*step*UP + ) + return result + + +class SpaceFillingCurveScene(Scene): + DEFAULT_CONFIG = { + "curve_class" : None #Must be filled in in subclasses + } + @staticmethod + def args_to_string(max_order): + return str(max_order) + + @staticmethod + def string_to_args(num_str): + return int(num_str) + +class SpaceFillingCurveGrowingOrder(SpaceFillingCurveScene): + def construct(self, max_order): + sample = self.curve_class(order = 1) + curve = Line(sample.radius*LEFT, sample.radius*RIGHT) + curve.gradient_highlight( + sample.start_color, + sample.end_color + ) + for order in range(1, max_order): + new_curve = self.curve_class(order = order) + self.play( + Transform(curve, new_curve), + run_time = 3/np.sqrt(order), + ) + self.dither() + + +class HilbertCurveGrowingOrder(SpaceFillingCurveGrowingOrder): + DEFAULT_CONFIG = { + "curve_class" : HilbertCurve, + } + +class SnakeCurveGrowingOrder(SpaceFillingCurveGrowingOrder): + DEFAULT_CONFIG = { + "curve_class" : SnakeCurve, + } + + +class DrawSpaceFillingCurve(SpaceFillingCurveScene): + def construct(self, order): + curve = self.curve_class(order = order) + self.play(ShowCreation(curve), run_time = 10) + self.dither() + +class DrawHilbertCurve(DrawSpaceFillingCurve): + DEFAULT_CONFIG = { + "curve_class" : HilbertCurve, + } + +class DrawSnakeCurve(DrawSpaceFillingCurve): + DEFAULT_CONFIG = { + "curve_class" : SnakeCurve, + } + + + + + + + + + + + + + + + diff --git a/mobject/__init__.py b/mobject/__init__.py index 8582942f..aadfc9b5 100644 --- a/mobject/__init__.py +++ b/mobject/__init__.py @@ -1,3 +1,7 @@ -from mobject import * -from image_mobject import * -from tex_mobject import * +__all__ = [ + "mobject", + "image_mobject", + "tex_mobject", +] + +from mobject import Mobject, Mobject1D, Mobject2D \ No newline at end of file diff --git a/mobject/mobject.py b/mobject/mobject.py index 7dc8861d..f9d98765 100644 --- a/mobject/mobject.py +++ b/mobject/mobject.py @@ -6,7 +6,6 @@ from PIL import Image from random import random from copy import deepcopy from colour import Color -import inspect import displayer as disp from helpers import * @@ -168,6 +167,19 @@ class Mobject(object): mob.rgbs[:,:] = rgb return self + def gradient_highlight(self, start_color, end_color): + start_rgb, end_rgb = [ + np.array(Color(color).get_rgb()) + for color in start_color, end_color + ] + for mob in self.get_full_submobject_family(): + num_points = len(mob.points) + mob.rgbs = np.array([ + interpolate(start_rgb, end_rgb, alpha) + for alpha in np.arange(num_points)/float(num_points) + ]) + return self + def filter_out(self, condition): for mob in self.get_full_submobject_family(): if len(mob.points) == 0: @@ -329,9 +341,14 @@ class Mobject(object): def get_all_points(self): return self.get_merged_array("points") + def get_all_rgbs(self): + return self.get_merged_array("rgbs") + def ingest_sub_mobjects(self): - for attr in self.get_array_attrs(): - setattr(self, attr, self.get_merged_array(attr)) + attrs = self.get_array_attrs() + arrays = map(self.get_merged_array, attrs) + for attr, array in zip(attrs, arrays): + setattr(self, attr, array) self.sub_mobjects = [] return self @@ -460,7 +477,7 @@ class Mobject(object): getattr(mobject2, attr), alpha)) -#TODO, Make the two implementations bellow not redundant +#TODO, Make the two implementations bellow non-redundant class Mobject1D(Mobject): DEFAULT_CONFIG = { "density" : DEFAULT_POINT_DENSITY_1D, @@ -489,18 +506,6 @@ class Mobject2D(Mobject): Mobject.__init__(self, **kwargs) -class Point(Mobject): - DEFAULT_CONFIG = { - "color" : BLACK, - } - def __init__(self, location = ORIGIN, **kwargs): - digest_locals(self) - Mobject.__init__(self, **kwargs) - - def generate_points(self): - self.add_points([self.location]) - - diff --git a/scene/__init__.py b/scene/__init__.py index f541bee2..47150083 100644 --- a/scene/__init__.py +++ b/scene/__init__.py @@ -1 +1,5 @@ -from scene import * \ No newline at end of file +__all__ = [ + "scene" +] + +from scene import Scene \ No newline at end of file diff --git a/topics/__init__.py b/topics/__init__.py index ade0283c..1ed0fc07 100644 --- a/topics/__init__.py +++ b/topics/__init__.py @@ -1,9 +1,11 @@ -from arithmetic import * -from characters import * -from combinatorics import * -from complex_numbers import * -from functions import * -from geometry import * -from graph_theory import * -from number_line import * -from three_dimensions import * \ No newline at end of file +__all__ = [ + "arithmetic", + "characters", + "combinatorics", + "complex_numbers", + "functions", + "geometry", + "graph_theory", + "number_line", + "three_dimensions", +] \ No newline at end of file diff --git a/topics/geometry.py b/topics/geometry.py index 3c001722..2b4f021e 100644 --- a/topics/geometry.py +++ b/topics/geometry.py @@ -3,6 +3,17 @@ from helpers import * from mobject import Mobject, Mobject1D +class Point(Mobject): + DEFAULT_CONFIG = { + "color" : BLACK, + } + def __init__(self, location = ORIGIN, **kwargs): + digest_locals(self) + Mobject.__init__(self, **kwargs) + + def generate_points(self): + self.add_points([self.location]) + class Dot(Mobject1D): #Use 1D density, even though 2D DEFAULT_CONFIG = { "radius" : 0.05