diff --git a/animation/animation.py b/animation/animation.py index 6935864f..b54116b8 100644 --- a/animation/animation.py +++ b/animation/animation.py @@ -112,27 +112,27 @@ class Animation(object): #Fuck this is cool! -class TransformAnimations(Transform): - def __init__(self, start_anim, end_anim, - alpha_func = squish_alpha_func(high_inflection_0_to_1), - **kwargs): - self.start_anim, self.end_anim = start_anim, end_anim - Transform.__init__( - self, - start_anim.mobject, - end_anim.mobject, - run_time = max(start_anim.run_time, end_anim.run_time), - alpha_func = alpha_func, - **kwargs - ) - #Rewire starting and ending mobjects - start_anim.mobject = self.starting_mobject - end_anim.mobject = self.ending_mobject +# class TransformAnimations(Transform): +# def __init__(self, start_anim, end_anim, +# alpha_func = squish_alpha_func(high_inflection_0_to_1), +# **kwargs): +# self.start_anim, self.end_anim = start_anim, end_anim +# Transform.__init__( +# self, +# start_anim.mobject, +# end_anim.mobject, +# run_time = max(start_anim.run_time, end_anim.run_time), +# alpha_func = alpha_func, +# **kwargs +# ) +# #Rewire starting and ending mobjects +# start_anim.mobject = self.starting_mobject +# end_anim.mobject = self.ending_mobject - def update(self, alpha): - self.start_anim.update(alpha) - self.end_anim.update(alpha) - Transform.update(self, alpha) +# def update(self, alpha): +# self.start_anim.update(alpha) +# self.end_anim.update(alpha) +# Transform.update(self, alpha) diff --git a/animation/transform.py b/animation/transform.py index 64defdd7..84ef506c 100644 --- a/animation/transform.py +++ b/animation/transform.py @@ -59,19 +59,21 @@ class Transform(Animation): ) class SemiCircleTransform(Transform): + def __init__(self, mobject1, mobject2, counterclockwise = True, + *args, **kwargs): + Transform.__init__(self, mobject1, mobject2, *args, **kwargs) + self.axis = (0, 0, 1) if counterclockwise else (0, 0, -1) + def update_mobject(self, alpha): sm, em = self.starting_mobject, self.ending_mobject midpoints = (sm.points + em.points) / 2 angle = alpha * np.pi - rotation_matrix = np.matrix([ - [np.cos(angle), np.sin(angle), 0], - [-np.sin(angle), np.cos(angle), 0], - [0, 0, 1], - ]) - self.mobject.points = np.dot( - sm.points - midpoints, - np.transpose(rotation_matrix) - ) + midpoints + rot_matrix = rotation_matrix(angle, self.axis)[:2, :2] + self.mobject.points[:,:2] = np.dot( + (sm.points - midpoints)[:,:2], + np.transpose(rot_matrix) + ) + midpoints[:,:2] + self.mobject.points[:,2] = (1-alpha)*sm.points[:,2] + alpha*em.points[:,2] self.mobject.rgbs = (1-alpha)*sm.rgbs + alpha*em.rgbs class FadeToColor(Transform): diff --git a/constants.py b/constants.py index ec5e732c..dbc62d0b 100644 --- a/constants.py +++ b/constants.py @@ -55,3 +55,9 @@ TEMPLATE_TEXT_FILE = os.path.join(TEX_DIR, "text_template.tex") LOGO_PATH = os.path.join(IMAGE_DIR, "logo.png") +DARK_BLUE = "#236B8E" +DARK_BROWN = "#8B4513" +LIGHT_BROWN = "#CD853F" + + + diff --git a/displayer.py b/displayer.py index 09040392..c75a15d2 100644 --- a/displayer.py +++ b/displayer.py @@ -33,7 +33,9 @@ def paint_region(region, image_array = None, color = None): return pixels def paint_mobject(mobject, image_array = None): - pixels = get_pixels(image_array) + pixels = get_pixels(image_array) + if mobject.get_num_points() == 0: + return pixels height = pixels.shape[0] width = pixels.shape[1] space_height = SPACE_HEIGHT @@ -48,9 +50,12 @@ def paint_mobject(mobject, image_array = None): points[:,1] *= -1 #Map points to pixel space, then create pixel array first in terms #of its flattened version - points += np.array( - [space_width, space_height]*points.shape[0] - ).reshape(points.shape) + try: + points += np.array( + [space_width, space_height]*points.shape[0] + ).reshape(points.shape) + except: + print points.shape, mobject.points.shape points *= np.array( [width / (2.0 * space_width), height / (2.0 * space_height)]*\ points.shape[0] diff --git a/mobject/__init__.py b/mobject/__init__.py index 8e05c010..c52e0b7e 100644 --- a/mobject/__init__.py +++ b/mobject/__init__.py @@ -3,3 +3,4 @@ from image_mobject import * from simple_mobjects import * from three_dimensional_mobjects import * from function_graphs import * +from creatures import * \ No newline at end of file diff --git a/mobject/creatures.py b/mobject/creatures.py new file mode 100644 index 00000000..840ad70d --- /dev/null +++ b/mobject/creatures.py @@ -0,0 +1,87 @@ +import numpy as np +import itertools as it +import os + +from image_mobject import * +from mobject import * +from simple_mobjects import * + + +class PiCreature(CompoundMobject): + DEFAULT_COLOR = "blue" + def __init__(self, color = DEFAULT_COLOR, **kwargs): + scale_val = 0.5 + mouth_to_eyes_distance = 0.25 + part_names = [ + 'arm', + 'body', + 'left_eye', + 'right_eye', + 'left_leg', + 'right_leg', + 'mouth', + ] + white_part_names = ['left_eye', 'right_eye', 'mouth'] + directory = os.path.join(IMAGE_DIR, "PiCreature") + + self.parts = [] + self.white_parts = [] + for part_name in part_names: + path = os.path.join(directory, "pi_creature_"+part_name) + path += ".png" + mob = ImageMobject(path) + mob.scale(scale_val) + if part_name in white_part_names: + self.white_parts.append(mob) + else: + mob.highlight(color) + setattr(self, part_name, mob) + self.parts.append(mob) + self.mouth.center().shift( + self.left_eye.get_center()/2 + + self.right_eye.get_center()/2 - + (0, mouth_to_eyes_distance, 0) + ) + self.reload_parts() + + def reload_parts(self): + CompoundMobject.__init__(self, *self.parts) + return self + + def highlight(self, color): + for part in set(self.parts).difference(self.white_parts): + part.highlight(color) + return self.reload_parts() + + def give_frown(self): + center = self.mouth.get_center() + self.mouth.center() + self.mouth.apply_function(lambda (x, y, z) : (x, -y, z)) + self.mouth.shift(center) + return self.reload_parts() + + def give_straight_face(self): + center = self.mouth.get_center() + self.mouth.center() + new_mouth = tex_mobject("-").scale(0.5) + new_mouth.center().shift(self.mouth.get_center()) + new_mouth.shift(center) + self.parts[self.parts.index(self.mouth)] = new_mouth + self.white_parts[self.white_parts.index(self.mouth)] = new_mouth + self.mouth = new_mouth + return self.reload_parts() + +class Randolph(PiCreature): + pass #Nothing more than an alternative name + +class Mortimer(PiCreature): + def __init__(self, *args, **kwargs): + PiCreature.__init__(self, *args, **kwargs) + self.highlight(DARK_BROWN) + self.give_straight_face() + + + + + + diff --git a/mobject/mobject.py b/mobject/mobject.py index be7bb12d..b654e83b 100644 --- a/mobject/mobject.py +++ b/mobject/mobject.py @@ -88,10 +88,23 @@ class Mobject(object): def shift(self, vector): cycle = it.cycle(vector) - v = np.array([cycle.next() for x in range(self.points.size)]).reshape(self.points.shape) + v = np.array([ + cycle.next() + for x in range(self.points.size) + ]).reshape(self.points.shape) self.points += v return self + def wag(self, wag_direction = [0, 1, 0], wag_axis = [-1, 0, 0]): + alphas = np.dot(self.points, np.transpose(wag_axis)) + alphas -= min(alphas) + alphas /= max(alphas) + self.points += np.dot( + alphas.reshape((len(alphas), 1)), + np.array(wag_direction).reshape((1, 3)) + ) + return self + def center(self): self.shift(-self.get_center()) return self @@ -116,9 +129,6 @@ class Mobject(object): self.add_points(points, rgbs) return self - def get_num_points(self): - return self.points.shape[0] - def pose_at_angle(self): self.rotate(np.pi / 7) self.rotate(np.pi / 7, [1, 0, 0]) @@ -155,6 +165,10 @@ class Mobject(object): return self ### Getters ### + + def get_num_points(self): + return self.points.shape[0] + def get_center(self): return np.apply_along_axis(np.mean, 0, self.points) diff --git a/mobject/simple_mobjects.py b/mobject/simple_mobjects.py index 124deb23..32c9e625 100644 --- a/mobject/simple_mobjects.py +++ b/mobject/simple_mobjects.py @@ -83,7 +83,7 @@ class Line(Mobject1D): def __init__(self, start, end, density = DEFAULT_POINT_DENSITY_1D, *args, **kwargs): self.start = np.array(start) self.end = np.array(end) - density *= np.linalg.norm(self.start - self.end) + density *= self.get_length() Mobject1D.__init__(self, density = density, *args, **kwargs) def generate_points(self): @@ -92,6 +92,16 @@ class Line(Mobject1D): for t in np.arange(0, 1, self.epsilon) ]) + def get_length(self): + return np.linalg.norm(self.start - self.end) + + def get_slope(self): + rise, run = [ + float(self.end[i] - self.start[i]) + for i in [1, 0] + ] + return rise/run + class CurvedLine(Line): def generate_points(self): equidistant_point = rotate_vector( diff --git a/moser/__init__.py b/moser/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/moser/images.py b/moser/images.py deleted file mode 100644 index b1745e5f..00000000 --- a/moser/images.py +++ /dev/null @@ -1,82 +0,0 @@ -#!/usr/bin/env python - -import numpy as np -import itertools as it -import operator as op -from copy import deepcopy -from random import random, randint -import sys -import inspect - - -from animation import * -from mobject import * -from image_mobject import * -from constants import * -from region import * -from scene import Scene -from script_wrapper import command_line_create_scene - -from moser_helpers import * -from graphs import * - -if __name__ == "__main__": - prefix = "moser_images/" - # cs_outer = CircleScene(RADIANS[:6]) - # cs_outer.highlight_region( - # Region(lambda x, y : x**2 + y**2 > RADIUS**2) - # ) - - # cs_graph = CircleScene(RADIANS) - # cs_graph.generate_intersection_dots() - # cs_graph.add(*cs_graph.intersection_dots) - # cs_graph.chop_lines_at_intersection_points() - # cs_graph.chop_circle_at_points() - # for line in cs_graph.lines: - # line.scale_in_place(0.5) - # for piece in cs_graph.smaller_circle_pieces: - # piece.highlight("yellow") - # cs_graph.remove(*cs_graph.circle_pieces) - # cs_graph.add(*cs_graph.smaller_circle_pieces) - - savable_things = [ - # (Mobject(), "Blackness") - # (tex_mobject(r"V-E+F=2"), "EulersFormula"), - # (PascalsTriangleScene(N_PASCAL_ROWS), "PascalsTriangle"), - # (tex_mobject(r"1, 2, 4, 8, 16, 31, \dots"), "FalsePattern"), - # ( - # tex_mobject(r""" - # \underbrace{1, 2, 4, 16, 31, 57, 99, 163, 256, 386, \dots}_{ - # \text{What is this pattern?} - # } - # """), - # "WhatIsThisPattern" - # ), - # (tex_mobject(r"n \choose k"), "NChooseK"), - # (GraphScene(SAMPLE_GRAPH), "SampleGraph"), - # (text_mobject("You don't even want me to draw this..."), "DontWantToDraw"), - # (tex_mobject(r"{100 \choose 2} = \frac{100 \cdot 99}{2} = 4950"), "100Choose2"), - # (text_mobject("What? You actually want me to draw it? Okay..."), "ReallyDontWant"), - # (text_mobject(r"There! You happy? \\ It's just one big blue blob."), "YouHappy"), - # ( - # tex_mobject( - # r"{100 \choose 4} = \frac{(100)(99)(98)(97)}{(1)(2)(3)(4)} = 3,921,225" - # ), - # "100Choose4" - # ), - # (text_mobject("Euler's Characteristic Formula"), "EF_Words"), - # (cs_outer, "OuterRegion"), - # (text_mobject("Pause and see if you can remember on your own!"), "Recap") - # (CircleScene([2*np.pi*random() for x in range(100)]), "CircleScene100") - # (text_mobject(r""" - # \textbf{Eul$\cdot$er's} (\text{oil}\textschwa\text{rz}), \emph{adj}: - # \begin{enumerate} - # \item Beautiful - # \item Demonstrating an unexpected logical aesthetic, especially in the context of mathematics. - # \end{enumerate} - # """), "EulersDefinition"), - # (cs_graph, "SuitableGraph"), - ] - for thing, name in savable_things: - thing.save_image(prefix + name) - diff --git a/moser/moser_helpers.py b/moser/moser_helpers.py deleted file mode 100644 index 4d6a4fa9..00000000 --- a/moser/moser_helpers.py +++ /dev/null @@ -1,128 +0,0 @@ -import numpy as np -import itertools as it - -from constants import * -from helpers import * -from image_mobject import * -from region import * -from scene import Scene - -from graphs import * - -RADIUS = SPACE_HEIGHT - 0.1 -CIRCLE_DENSITY = DEFAULT_POINT_DENSITY_1D*RADIUS -MOVIE_PREFIX = "moser/" -RADIANS = np.arange(0, 6, 6.0/7) -MORE_RADIANS = np.append(RADIANS, RADIANS + 0.5) -N_PASCAL_ROWS = 7 -BIG_N_PASCAL_ROWS = 11 - -############################################ - -class CircleScene(Scene): - args_list = [ - (RADIANS[:x],) - for x in range(1, len(RADIANS)+1) - ] - @staticmethod - def args_to_string(*args): - return str(len(args[0])) #Length of radians - - def __init__(self, radians, radius = RADIUS, *args, **kwargs): - Scene.__init__(self, *args, **kwargs) - self.radius = radius - self.circle = Circle(density = CIRCLE_DENSITY).scale(self.radius) - self.points = [ - (self.radius * np.cos(angle), self.radius * np.sin(angle), 0) - for angle in radians - ] - self.dots = [Dot(point) for point in self.points] - self.lines = [Line(p1, p2) for p1, p2 in it.combinations(self.points, 2)] - self.n_equals = tex_mobject( - "n=%d"%len(radians), - ).shift((-SPACE_WIDTH+1, SPACE_HEIGHT-1.5, 0)) - self.add(self.circle, self.n_equals, *self.dots + self.lines) - - - def generate_intersection_dots(self): - """ - Generates and adds attributes intersection_points and - intersection_dots, but does not yet add them to the scene - """ - self.intersection_points = [ - intersection((p[0], p[2]), (p[1], p[3])) - for p in it.combinations(self.points, 4) - ] - self.intersection_dots = [ - Dot(point) for point in self.intersection_points - ] - - def chop_lines_at_intersection_points(self): - if not hasattr(self, "intersection_dots"): - self.generate_intersection_dots() - self.remove(*self.lines) - self.lines = [] - for point_pair in it.combinations(self.points, 2): - int_points = filter( - lambda p : is_on_line(p, *point_pair), - self.intersection_points - ) - points = list(point_pair) + int_points - points = map(lambda p : (p[0], p[1], 0), points) - points.sort(cmp = lambda x,y: cmp(x[0], y[0])) - self.lines += [ - Line(points[i], points[i+1]) - for i in range(len(points)-1) - ] - self.add(*self.lines) - - def chop_circle_at_points(self): - self.remove(self.circle) - self.circle_pieces = [] - self.smaller_circle_pieces = [] - for i in range(len(self.points)): - pp = self.points[i], self.points[(i+1)%len(self.points)] - transform = np.array([ - [pp[0][0], pp[1][0], 0], - [pp[0][1], pp[1][1], 0], - [0, 0, 1] - ]) - circle = deepcopy(self.circle) - smaller_circle = deepcopy(self.circle) - for c in circle, smaller_circle: - c.points = np.dot( - c.points, - np.transpose(np.linalg.inv(transform)) - ) - c.filter_out( - lambda p : p[0] < 0 or p[1] < 0 - ) - if c == smaller_circle: - c.filter_out( - lambda p : p[0] > 4*p[1] or p[1] > 4*p[0] - ) - c.points = np.dot( - c.points, - np.transpose(transform) - ) - self.circle_pieces.append(circle) - self.smaller_circle_pieces.append(smaller_circle) - self.add(*self.circle_pieces) - - def generate_regions(self): - self.regions = plane_partition_from_points(*self.points) - interior = Region(lambda x, y : x**2 + y**2 < self.radius**2) - map(lambda r : r.intersect(interior), self.regions) - self.exterior = interior.complement() - - -################################################## - -def int_list_to_string(int_list): - return "-".join(map(str, int_list)) - -def moser_function(n): - return choose(n, 4) + choose(n, 2) + 1 - - - diff --git a/scene/graphs.py b/scene/graphs.py index 634ec759..966f5fd7 100644 --- a/scene/graphs.py +++ b/scene/graphs.py @@ -38,17 +38,20 @@ CUBE_GRAPH = { SAMPLE_GRAPH = { "name" : "SampleGraph", - # 4 2 3 + # 4 2 3 8 # 0 1 - # - # 5 + # 7 + # 5 6 "vertices" :[ ( 0, 0, 0), ( 2, 0, 0), - ( 1, 1, 0), - ( 3, 1, 0), - (-1, 1, 0), + ( 1, 2, 0), + ( 3, 2, 0), + (-1, 2, 0), (-2,-2, 0), + ( 2,-2, 0), + ( 4,-1, 0), + ( 6, 2, 0), ], "edges" : [ (0, 1), @@ -61,6 +64,11 @@ SAMPLE_GRAPH = { (4, 5), (0, 5), (1, 5), + (5, 6), + (6, 7), + (7, 1), + (7, 8), + (8, 3), ], "region_cycles" : [ (0, 1, 2), @@ -68,7 +76,9 @@ SAMPLE_GRAPH = { (2, 4, 0), (4, 5, 0), (0, 5, 1), - (4, 5, 1, 3), + (1, 5, 6, 7), + (1, 7, 8, 3), + (4, 5, 6, 7, 8), ] } diff --git a/scene/sub_scenes.py b/scene/sub_scenes.py index cf6bef51..43698559 100644 --- a/scene/sub_scenes.py +++ b/scene/sub_scenes.py @@ -6,6 +6,7 @@ from graphs import * from mobject import * from animation import * +from region import * from constants import * from helpers import * @@ -20,14 +21,16 @@ class GraphScene(Scene): return args[0]["name"] def __init__(self, graph, *args, **kwargs): - Scene.__init__(self, *args, **kwargs) #See CUBE_GRAPH above for format of graph self.graph = graph - self.points = map(np.array, graph["vertices"]) + Scene.__init__(self, *args, **kwargs) + + def construct(self): + self.points = map(np.array, self.graph["vertices"]) self.vertices = self.dots = [Dot(p) for p in self.points] self.edges = [ Line(self.points[i], self.points[j]) - for i, j in graph["edges"] + for i, j in self.graph["edges"] ] self.add(*self.dots + self.edges) @@ -45,6 +48,50 @@ class GraphScene(Scene): regions[-1].complement()#Outer region painted outwardly... self.regions = regions + def generate_spanning_tree(self): + pass + + def draw_vertices(self): + self.clear() + self.animate(ShowCreation(CompoundMobject(*self.vertices))) + + def draw_edges(self): + self.animate(*[ + ShowCreation(edge, run_time = 1.0) + for edge in self.edges + ]) + + def replace_vertices_with(self, mobject): + mobject.center() + diameter = max(mobject.get_height(), mobject.get_width()) + self.animate(*[ + SemiCircleTransform( + vertex, + deepcopy(mobject).shift(vertex.get_center()) + ) + for vertex in self.vertices + ] + [ + ApplyMethod( + edge.scale_in_place, + (edge.get_length() - diameter) / edge.get_length() + ) + for edge in self.edges + ]) + + def annotate_edges(self, mobject): + angles = map(np.arctan, map(Line.get_slope, self.edges)) + self.edge_annotations = [ + deepcopy(mobject).rotate(angle).shift(edge.get_center()) + for angle, edge in zip(angles, self.edges) + ] + self.animate(*[ + FadeIn(ann) + for ann in self.edge_annotations + ]) + + + + BIG_N_PASCAL_ROWS = 11 N_PASCAL_ROWS = 7 class PascalsTriangleScene(Scene): diff --git a/script_wrapper.py b/script_wrapper.py index 1e3968d5..48830bcb 100644 --- a/script_wrapper.py +++ b/script_wrapper.py @@ -63,7 +63,7 @@ def find_scene_class_and_args(scene_string, args_extension, sys.exit(0) return possible_results[index] -def command_line_create_scene(sys_argv, movie_prefix = ""): +def command_line_create_scene(movie_prefix = ""): scene_classes = [ pair[1] for pair in inspect.getmembers( @@ -74,7 +74,7 @@ def command_line_create_scene(sys_argv, movie_prefix = ""): ) ] try: - opts, args = getopt.getopt(sys_argv, "h:l:p") + opts, args = getopt.getopt(sys.argv[1:], 'hlps') except getopt.GetoptError as err: print str(err) sys.exit(2) @@ -82,7 +82,7 @@ def command_line_create_scene(sys_argv, movie_prefix = ""): scene_string = "" args_extension = "" display_config = PRODUCTION_QUALITY_DISPLAY_CONFIG - preview = False + action = "write" for opt, arg in opts: if opt == '-h': @@ -92,7 +92,9 @@ def command_line_create_scene(sys_argv, movie_prefix = ""): display_config = LOW_QUALITY_DISPLAY_CONFIG elif opt == '-p': display_config = LOW_QUALITY_DISPLAY_CONFIG - preview = True + action = "preview" + elif opt == '-s': + action = "show_frame" if len(args) > 0: scene_string = args[0] if len(args) > 1: @@ -105,10 +107,14 @@ def command_line_create_scene(sys_argv, movie_prefix = ""): name = SceneClass.__name__ + SceneClass.args_to_string(*args) print "Constructing %s..."%name scene = SceneClass(*args, display_config = display_config) - if preview: - scene.preview() - else: + if action == "write": scene.write_to_movie(movie_prefix + name) + elif action == "preview": + scene.preview() + elif action == "show_frame": + scene.show_frame() + + diff --git a/scripts/ecf_graph_scenes.py b/scripts/ecf_graph_scenes.py new file mode 100644 index 00000000..78e71235 --- /dev/null +++ b/scripts/ecf_graph_scenes.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python + +import numpy as np +import itertools as it +from copy import deepcopy +import sys + + +from animation import * +from mobject import * +from constants import * +from region import * +from scene import Scene, GraphScene +from moser_main import EulersFormula +from script_wrapper import command_line_create_scene + +MOVIE_PREFIX = "ecf_graph_scenes/" + +class IntroduceGraphs(GraphScene): + def construct(self): + GraphScene.construct(self) + self.draw_vertices() + self.draw_edges() + self.dither() + self.clear() + self.add(*self.edges) + self.replace_vertices_with(SimpleFace().scale(0.4)) + friends = text_mobject("Friends").scale(0.5) + self.annotate_edges(friends.shift((0, friends.get_height()/2, 0))) + self.animate(*[ + SemiCircleTransform(vertex, Dot(point)) + for vertex, point in zip(self.vertices, self.points) + ]+[ + Transform(ann, line) + for ann, line in zip( + self.edge_annotations, + self.edges + ) + ]) + self.dither() + +class PlanarGraphDefinition(Scene): + def construct(self): + + + +if __name__ == "__main__": + command_line_create_scene(MOVIE_PREFIX) \ No newline at end of file diff --git a/generate_logo.py b/scripts/generate_logo.py similarity index 92% rename from generate_logo.py rename to scripts/generate_logo.py index d76e10d6..e7b599d4 100644 --- a/generate_logo.py +++ b/scripts/generate_logo.py @@ -1,24 +1,19 @@ #!/usr/bin/env python from PIL import Image +import numpy as np + from animation import * from mobject import * from constants import * from helpers import * from scene import * -from image_mobject import * import itertools as it import os -import numpy as np - -DARK_BLUE = "#236B8E" -DARK_BROWN = "#8B4513" -LIGHT_BROWN = "#CD853F" LOGO_RADIUS = 1.5 - if __name__ == '__main__': circle = Circle(density = 100, color = 'skyblue').repeat(5).scale(LOGO_RADIUS) sphere = Sphere(density = 50, color = DARK_BLUE).scale(LOGO_RADIUS) diff --git a/scripts/moser_intro.py b/scripts/moser_intro.py index b851c3a5..e856dfd5 100644 --- a/scripts/moser_intro.py +++ b/scripts/moser_intro.py @@ -8,7 +8,6 @@ from copy import deepcopy from animation import * from mobject import * -from image_mobject import * from constants import * from region import * from scene import Scene diff --git a/scripts/moser_main.py b/scripts/moser_main.py index c3873cb2..7f2c2501 100644 --- a/scripts/moser_main.py +++ b/scripts/moser_main.py @@ -11,14 +11,123 @@ import inspect from animation import * from mobject import * -from image_mobject import * from constants import * from region import * -from scene import Scene +from scene import Scene, GraphScene, PascalsTriangleScene from script_wrapper import command_line_create_scene -from moser_helpers import * -from graphs import * +RADIUS = SPACE_HEIGHT - 0.1 +CIRCLE_DENSITY = DEFAULT_POINT_DENSITY_1D*RADIUS +MOVIE_PREFIX = "moser/" +RADIANS = np.arange(0, 6, 6.0/7) +MORE_RADIANS = np.append(RADIANS, RADIANS + 0.5) +N_PASCAL_ROWS = 7 +BIG_N_PASCAL_ROWS = 11 + +def int_list_to_string(int_list): + return "-".join(map(str, int_list)) + +def moser_function(n): + return choose(n, 4) + choose(n, 2) + 1 + +########################################### + +class CircleScene(Scene): + args_list = [ + (RADIANS[:x],) + for x in range(1, len(RADIANS)+1) + ] + @staticmethod + def args_to_string(*args): + return str(len(args[0])) #Length of radians + + def __init__(self, radians, radius = RADIUS, *args, **kwargs): + Scene.__init__(self, *args, **kwargs) + self.radius = radius + self.circle = Circle(density = CIRCLE_DENSITY).scale(self.radius) + self.points = [ + (self.radius * np.cos(angle), self.radius * np.sin(angle), 0) + for angle in radians + ] + self.dots = [Dot(point) for point in self.points] + self.lines = [Line(p1, p2) for p1, p2 in it.combinations(self.points, 2)] + self.n_equals = tex_mobject( + "n=%d"%len(radians), + ).shift((-SPACE_WIDTH+1, SPACE_HEIGHT-1.5, 0)) + self.add(self.circle, self.n_equals, *self.dots + self.lines) + + + def generate_intersection_dots(self): + """ + Generates and adds attributes intersection_points and + intersection_dots, but does not yet add them to the scene + """ + self.intersection_points = [ + intersection((p[0], p[2]), (p[1], p[3])) + for p in it.combinations(self.points, 4) + ] + self.intersection_dots = [ + Dot(point) for point in self.intersection_points + ] + + def chop_lines_at_intersection_points(self): + if not hasattr(self, "intersection_dots"): + self.generate_intersection_dots() + self.remove(*self.lines) + self.lines = [] + for point_pair in it.combinations(self.points, 2): + int_points = filter( + lambda p : is_on_line(p, *point_pair), + self.intersection_points + ) + points = list(point_pair) + int_points + points = map(lambda p : (p[0], p[1], 0), points) + points.sort(cmp = lambda x,y: cmp(x[0], y[0])) + self.lines += [ + Line(points[i], points[i+1]) + for i in range(len(points)-1) + ] + self.add(*self.lines) + + def chop_circle_at_points(self): + self.remove(self.circle) + self.circle_pieces = [] + self.smaller_circle_pieces = [] + for i in range(len(self.points)): + pp = self.points[i], self.points[(i+1)%len(self.points)] + transform = np.array([ + [pp[0][0], pp[1][0], 0], + [pp[0][1], pp[1][1], 0], + [0, 0, 1] + ]) + circle = deepcopy(self.circle) + smaller_circle = deepcopy(self.circle) + for c in circle, smaller_circle: + c.points = np.dot( + c.points, + np.transpose(np.linalg.inv(transform)) + ) + c.filter_out( + lambda p : p[0] < 0 or p[1] < 0 + ) + if c == smaller_circle: + c.filter_out( + lambda p : p[0] > 4*p[1] or p[1] > 4*p[0] + ) + c.points = np.dot( + c.points, + np.transpose(transform) + ) + self.circle_pieces.append(circle) + self.smaller_circle_pieces.append(smaller_circle) + self.add(*self.circle_pieces) + + def generate_regions(self): + self.regions = plane_partition_from_points(*self.points) + interior = Region(lambda x, y : x**2 + y**2 < self.radius**2) + map(lambda r : r.intersect(interior), self.regions) + self.exterior = interior.complement() + class CountSections(CircleScene): def __init__(self, *args, **kwargs): @@ -688,8 +797,14 @@ class EulersFormula(GraphScene): for d in self.dots ] colored_edges = [ - deepcopy(e).highlight("red") + CompoundMobject( + Line(midpoint, start), + Line(midpoint, end), + ).highlight("red") for e in self.edges + for start, end, midpoint in [ + (e.start, e.end, (e.start + e.end)/2) + ] ] frame_time = 0.3 @@ -1585,7 +1700,7 @@ class IntersectionChoppingExamples(Scene): ################################################## if __name__ == "__main__": - command_line_create_scene(sys.argv[1:], MOVIE_PREFIX) + command_line_create_scene(MOVIE_PREFIX)