diff --git a/Images/logo.png b/Images/logo.png new file mode 100644 index 00000000..58d941a6 Binary files /dev/null and b/Images/logo.png differ diff --git a/constants.py b/constants.py index 87da6960..0820efa3 100644 --- a/constants.py +++ b/constants.py @@ -32,7 +32,7 @@ DEFAULT_DITHER_TIME = 1.0 DEFAULT_NUM_STARS = 1000 SPACE_HEIGHT = 4.0 -SPACE_WIDTH = DEFAULT_WIDTH * DEFAULT_HEIGHT / DEFAULT_WIDTH +SPACE_WIDTH = SPACE_HEIGHT * DEFAULT_WIDTH / DEFAULT_HEIGHT THIS_DIR = os.path.dirname(os.path.realpath(__file__)) IMAGE_DIR = os.path.join(THIS_DIR, "images") diff --git a/image_mobject.py b/image_mobject.py index dc9eacbd..21fa70c3 100644 --- a/image_mobject.py +++ b/image_mobject.py @@ -96,16 +96,17 @@ def tex_mobject(expression, size = r"\HUGE"): return tex_mobjects(expression, size) def tex_mobjects(expression, size = r"\HUGE"): + scale_value = 2 images = tex_to_image(expression, size) if isinstance(images, list): #TODO, is checking listiness really the best here? - result = [ImageMobject(im).scale(2) for im in images] + result = [ImageMobject(im).scale(scale_value) for im in images] center = CompoundMobject(*result).get_center() for mob in result: mob.shift(-center) return result else: - return ImageMobject(images).center().scale(2) + return ImageMobject(images).center().scale(scale_value) diff --git a/moser/main.py b/moser/main.py index 2483240c..66d80501 100644 --- a/moser/main.py +++ b/moser/main.py @@ -629,6 +629,66 @@ class PascalsTriangleWithNChooseK(PascalsTriangleScene): self.remove(*self.mobjects) self.add(*[mob_dicts[1-i][n][k] for n, k in self.coords]) +class PascalsTriangleNChooseKExample(PascalsTriangleScene): + args_list = [ + (N_PASCAL_ROWS, 5, 3), + ] + @staticmethod + def args_to_string(nrows, n, k): + return "%d_n=%d_k=%d"%(nrows, n, k) + + def __init__(self, nrows, n, k, *args, **kwargs): + PascalsTriangleScene.__init__(self, nrows, *args, **kwargs) + dither_time = 0.5 + triangle_terms = [self.coords_to_mobs[a][b] for a, b in self.coords] + formula_terms = left, n_mob, k_mob, right = tex_mobject([ + r"\left(", str(n), r"\atop %d"%k, r"\right)" + ]) + formula_center = (SPACE_WIDTH - 1, SPACE_HEIGHT - 1, 0) + self.remove(*triangle_terms) + self.add(*formula_terms) + self.dither() + self.animate(* + [ + ShowCreation(mob) for mob in triangle_terms + ]+[ + ApplyMethod((Mobject.shift, formula_center), mob) + for mob in formula_terms + ], + run_time = 1.0 + ) + self.remove(n_mob, k_mob) + for a in range(n+1): + row = [self.coords_to_mobs[a][b] for b in range(a+1)] + a_mob = tex_mobject(str(a)) + a_mob.shift(n_mob.get_center()) + a_mob.highlight("green") + self.add(a_mob) + for mob in row: + mob.highlight("green") + self.dither(dither_time) + if a < n: + for mob in row: + mob.highlight("white") + self.remove(a_mob) + self.dither() + for b in range(k+1): + b_mob = tex_mobject(str(b)) + b_mob.shift(k_mob.get_center()) + b_mob.highlight("yellow") + self.add(b_mob) + self.coords_to_mobs[n][b].highlight("yellow") + self.dither(dither_time) + if b < k: + self.coords_to_mobs[n][b].highlight("green") + self.remove(b_mob) + self.animate(*[ + ApplyMethod((Mobject.fade, 0.2), mob) + for mob in triangle_terms + if mob != self.coords_to_mobs[n][k] + ]) + self.dither() + class PascalsTriangleSumRows(PascalsTriangleScene): def __init__(self, *args, **kwargs): PascalsTriangleScene.__init__(self, *args, **kwargs) @@ -690,6 +750,91 @@ class PascalsTriangleSumRows(PascalsTriangleScene): self.add(powers_of_two_symbols[n]) +class MoserSolutionInPascal(PascalsTriangleScene): + args_list = [ + (N_PASCAL_ROWS, n) + for n in range(3, 8) + ] + [ + (BIG_N_PASCAL_ROWS, 10) + ] + @staticmethod + def args_to_string(nrows, n): + return "%d_n=%d"%(nrows,n) + + def __init__(self, nrows, n, *args, **kwargs): + PascalsTriangleScene.__init__(self, nrows, *args, **kwargs) + term_color = "green" + self.generate_n_choose_k_mobs() + self.remove(*[self.coords_to_mobs[n0][k0] for n0, k0 in self.coords]) + terms = one, plus0, n_choose_2, plus1, n_choose_4 = tex_mobjects([ + "1", "+", r"{%d \choose 2}"%n, "+", r"{%d \choose 4}"%n + ]) + target_terms = [] + for k in range(len(terms)): + if k%2 == 0 and k <= n: + new_term = deepcopy(self.coords_to_n_choose_k[n][k]) + new_term.highlight(term_color) + else: + new_term = Point( + self.coords_to_center(n, k) + ) + target_terms.append(new_term) + self.add(*terms) + self.dither() + self.animate(* + [ + FadeIn(self.coords_to_n_choose_k[n0][k0]) + for n0, k0 in self.coords + if (n0, k0) not in [(n, 0), (n, 2), (n, 4)] + ]+[ + Transform(term, target_term) + for term, target_term in zip(terms, target_terms) + ] + ) + self.dither() + term_range = range(0, min(4, n)+1, 2) + target_terms = dict([ + (k, deepcopy(self.coords_to_mobs[n][k]).highlight(term_color)) + for k in term_range + ]) + self.animate(* + [ + SemiCircleTransform( + self.coords_to_n_choose_k[n0][k0], + self.coords_to_mobs[n0][k0] + ) + for n0, k0 in self.coords + if (n0, k0) not in [(n, k) for k in term_range] + ]+[ + SemiCircleTransform(terms[k], target_terms[k]) + for k in term_range + ] + ) + self.dither() + for k in term_range: + if k == 0: + above_terms = [self.coords_to_n_choose_k[n-1][k]] + elif k == n: + above_terms = [self.coords_to_n_choose_k[n-1][k-1]] + else: + above_terms = [ + self.coords_to_n_choose_k[n-1][k-1], + self.coords_to_n_choose_k[n-1][k], + ] + self.add(self.coords_to_mobs[n][k]) + self.animate(Transform( + terms[k], + CompoundMobject(*above_terms).highlight(term_color) + )) + self.remove(*above_terms) + self.dither() + terms_sum = tex_mobject(str(moser_function(n))) + terms_sum.shift((SPACE_WIDTH-1, terms[0].get_center()[1], 0)) + terms_sum.highlight(term_color) + self.animate(Transform(CompoundMobject(*terms), terms_sum)) + + + ################################################## if __name__ == "__main__": diff --git a/moser/main.py~ b/moser/main.py~ new file mode 100644 index 00000000..e3b90107 --- /dev/null +++ b/moser/main.py~ @@ -0,0 +1,518 @@ +#!/usr/bin/env python + +import numpy as np +import itertools as it +import operator as op +from copy import deepcopy +from random import random + + +from animation import * +from mobject import * +from image_mobject import * +from constants import * +from region import * +from scene import Scene + +from moser_helpers import * +from graphs import * + +RADIUS = SPACE_HEIGHT - 0.1 +CIRCLE_DENSITY = DEFAULT_POINT_DENSITY_1D*RADIUS + +movie_prefix = "moser/" + +############################################ + +class CircleScene(Scene): + def __init__(self, radians, *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.add(self.circle, *self.dots + self.lines) + +class GraphScene(Scene): + #Note, the placement of vertices in this is pretty hard coded, be + #warned if you want to change it. + 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"]) + 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"] + ] + self.add(*self.dots + self.edges) + + def generate_regions(self): + regions = [ + region_from_line_boundary(*[ + [ + self.points[rc[i]], + self.points[rc[(i+1)%len(rc)]] + ] + for i in range(len(rc)) + ]) + for rc in self.graph["region_cycles"] + ] + regions[-1].complement()#Outer region painted outwardly... + self.regions = regions + +################################################## + +def count_lines(*radians): + #TODO, Count things explicitly? + sc = CircleScene(radians) + text_center = (sc.radius + 1, sc.radius -1, 0) + scale_factor = 0.4 + text = tex_mobject(r"\text{How Many Lines?}", size = r"\large") + n = len(radians) + formula, answer = tex_mobject([ + r"{%d \choose 2} = \frac{%d(%d - 1)}{2} = "%(n, n, n), + str(choose(n, 2)) + ]) + text.scale(scale_factor).shift(text_center) + x = text_center[0] + new_lines = [ + Line((x-1, y, 0), (x+1, y, 0)) + for y in np.arange( + -(sc.radius - 1), + sc.radius - 1, + (2*sc.radius - 2)/len(sc.lines) + ) + ] + sc.add(text) + sc.dither() + sc.animate(*[ + Transform(line1, line2, run_time = 2) + for line1, line2 in zip(sc.lines, new_lines) + ]) + sc.dither() + sc.remove(text) + sc.count(new_lines) + anims = [FadeIn(formula)] + for mob in sc.mobjects: + if mob == sc.number: #put in during animate_count + anims.append(Transform(mob, answer)) + else: + anims.append(FadeOut(mob)) + sc.animate(*anims, run_time = 1) + sc.write_to_movie(movie_prefix + "CountLines%dPoints"%len(radians)) + + +def count_intersection_points(*radians): + radians = [r % (2*np.pi) for r in radians] + radians.sort() + sc = CircleScene(radians) + intersection_points = [ + intersection((p[0], p[2]), (p[1], p[3])) + for p in it.combinations(sc.points, 4) + ] + intersection_dots = [Dot(point) for point in intersection_points] + text_center = (sc.radius + 0.5, sc.radius -0.5, 0) + size = r"\large" + scale_factor = 0.4 + text = tex_mobject(r"\text{How Many Intersection Points?}", size = size) + n = len(radians) + formula, answer = tex_mobjects([ + r"{%d \choose 4} = \frac{%d(%d - 1)(%d - 2)(%d-3)}{1\cdot 2\cdot 3 \cdot 4}="%(n, n, n, n, n), + str(choose(n, 4)) + ]) + text.scale(scale_factor).shift(text_center) + # new_points = [ + # (text_center[0], y, 0) + # for y in np.arange( + # -(sc.radius - 1), + # sc.radius - 1, + # (2*sc.radius - 2)/choose(len(sc.points), 4) + # ) + # ] + # new_dots = CompoundMobject(*[ + # Dot(point) for point in new_points + # ]) + + sc.add(text) + sc.count(intersection_dots, "show", num_offset = (0, 0, 0)) + sc.dither() + # sc.animate(Transform(intersection_dots, new_dots)) + anims = [] + for mob in sc.mobjects: + if mob == sc.number: #put in during animate_count + anims.append(Transform(mob, answer)) + else: + anims.append(FadeOut(mob)) + anims.append(Animation(formula)) + sc.animate(*anims, run_time = 1) + + name = "CountIntersectionPoints%dPoints"%len(radians) + sc.write_to_movie(movie_prefix + name) + +def non_general_position(): + radians = np.arange(1, 7) + new_radians = (np.pi/3)*radians + sc1 = CircleScene(radians) + sc2 = CircleScene(new_radians) + center_region = reduce( + Region.intersect, + [ + HalfPlane((sc1.points[x], sc1.points[(x+3)%6])) + for x in [0, 4, 2]#Ya know, trust it + ] + ) + center_region + text = tex_mobject(r"\text{This region disappears}", size = r"\large") + text.center().scale(0.5).shift((-sc1.radius, sc1.radius-0.3, 0)) + arrow = Arrow( + point = (-0.35, -0.1, 0), + direction = (1, -1, 0), + length = sc1.radius + 1, + color = "white", + ) + + sc1.highlight_region(center_region, "green") + sc1.add(text, arrow) + sc1.dither(2) + sc1.remove(text, arrow) + sc1.reset_background() + sc1.animate(*[ + Transform(mob1, mob2, run_time = DEFAULT_ANIMATION_RUN_TIME) + for mob1, mob2 in zip(sc1.mobjects, sc2.mobjects) + ]) + sc1.write_to_movie(movie_prefix + "NonGeneralPosition") + +def line_corresponds_with_pair(radians, r1, r2): + sc = CircleScene(radians) + #Remove from sc.lines list, so they won't be faded out + assert r1 in radians and r2 in radians + line_index = list(it.combinations(radians, 2)).index((r1, r2)) + radians = list(radians) + dot0_index, dot1_index = radians.index(r1), radians.index(r2) + line, dot0, dot1 = sc.lines[line_index], sc.dots[dot0_index], sc.dots[dot1_index] + sc.lines.remove(line) + sc.dots.remove(dot0) + sc.dots.remove(dot1) + sc.dither() + sc.animate(*[ + FadeOut(mob, alpha_func = not_quite_there) + for mob in sc.lines + sc.dots + ]) + sc.add(sc.circle) + sc.animate(*[ + ScaleInPlace(mob, 3, alpha_func = there_and_back) + for mob in (dot0, dot1) + ]) + sc.animate(Transform(line, dot0)) + name = "LineCorrspondsWithPair%d%d"%(dot0_index, dot1_index) + sc.write_to_movie(movie_prefix + name) + +def illustrate_n_choose_k(n, k): + sc = Scene() + nrange = range(1, n+1) + tuples = list(it.combinations(nrange, k)) + nrange_mobs = tex_mobjects([str(n) + r'\;' for n in nrange]) + tuple_mobs = tex_mobjects( + [ + (r'\\&' if c%(20//k) == 0 else r'\;\;') + str(p) + for p, c in zip(tuples, it.count()) + ], + size = r"\small" + ) + tuple_terms = { + 2 : "pairs", + 3 : "triplets", + 4 : "quadruplets", + } + tuple_term = tuple_terms[k] if k in tuple_terms else "tuples" + form1, count, form2 = tex_mobject([ + r"{%d \choose %d} = "%(n, k), + "%d"%choose(n, k), + r" \text{ total %s}"%tuple_term + ]) + for mob in nrange_mobs: + mob.shift((0, 2, 0)) + for mob in form1, count, form2: + mob.shift((0, -SPACE_HEIGHT + 1, 0)) + count_center = count.get_center() + for mob in tuple_mobs: + mob.scale(0.6) + + sc.add(*nrange_mobs) + sc.dither() + run_time = 6.0 + frame_time = run_time / len(tuples) + for tup, count in zip(tuples, it.count()): + count_mob = tex_mobject(str(count+1)) + count_mob.center().shift(count_center) + sc.add(count_mob) + tuple_copy = CompoundMobject(*[nrange_mobs[index-1] for index in tup]) + tuple_copy.highlight() + sc.add(tuple_copy) + sc.add(tuple_mobs[count]) + sc.dither(frame_time) + sc.remove(count_mob) + sc.remove(tuple_copy) + sc.add(count_mob) + sc.animate(FadeIn(CompoundMobject(form1, form2))) + sc.write_to_movie(movie_prefix + "Illustrate%dChoose%d"%(n, k)) + +def intersection_point_correspondances(radians, indices): + assert(len(indices) == 4) + indices.sort() + sc = CircleScene(radians) + intersection_point = intersection( + (sc.points[indices[0]], sc.points[indices[2]]), + (sc.points[indices[1]], sc.points[indices[3]]) + ) + intersection_point = tuple(list(intersection_point) + [0]) + intersection_dot = Dot(intersection_point) + intersection_dot_arrow = Arrow(intersection_point).nudge() + sc.add(intersection_dot) + pairs = list(it.combinations(range(len(radians)), 2)) + lines_to_save = [ + sc.lines[pairs.index((indices[p0], indices[p1]))] + for p0, p1 in [(0, 2), (1, 3)] + ] + dots_to_save = [ + sc.dots[p] + for p in indices + ] + line_statement = tex_mobject(r"\text{Pair of Lines}") + dots_statement = tex_mobject(r"&\text{Quadruplet of} \\ &\text{outer dots}") + for mob in line_statement, dots_statement: + mob.center() + mob.scale(0.7) + mob.shift((SPACE_WIDTH-2, SPACE_HEIGHT - 1, 0)) + fade_outs = [] + line_highlights = [] + dot_highlights = [] + dot_pointers = [] + for mob in sc.mobjects: + if mob in lines_to_save: + line_highlights.append(Highlight(mob)) + elif mob in dots_to_save: + dot_highlights.append(Highlight(mob)) + dot_pointers.append(Arrow(mob.get_center()).nudge()) + elif mob != intersection_dot: + fade_outs.append(FadeOut(mob, alpha_func = not_quite_there)) + + sc.add(intersection_dot_arrow) + sc.animate(Highlight(intersection_dot)) + sc.remove(intersection_dot_arrow) + sc.animate(*fade_outs) + sc.dither() + sc.add(line_statement) + sc.animate(*line_highlights) + sc.remove(line_statement) + sc.dither() + sc.add(dots_statement, *dot_pointers) + sc.animate(*dot_highlights) + sc.remove(dots_statement, *dot_pointers) + + name = "IntersectionPointCorrespondances" + for ind in indices: + name += str(ind) + sc.write_to_movie(movie_prefix + name) + +def lines_intersect_outside(radians, indices): + assert(len(indices) == 4) + indices.sort() + sc = CircleScene(radians) + intersection_point = intersection( + (sc.points[indices[0]], sc.points[indices[1]]), + (sc.points[indices[2]], sc.points[indices[3]]) + ) + intersection_point = tuple(list(intersection_point) + [0]) + intersection_dot = Dot(intersection_point) + pairs = list(it.combinations(range(len(radians)), 2)) + lines_to_save = [ + sc.lines[pairs.index((indices[p0], indices[p1]))] + for p0, p1 in [(0, 1), (2, 3)] + ] + sc.animate(*[ + FadeOut(mob, alpha_func = not_quite_there) + for mob in sc.mobjects if mob not in lines_to_save + ]) + sc.animate(*[ + Transform( + Line(sc.points[indices[p0]], sc.points[indices[p1]]), + Line(sc.points[indices[p0]], intersection_point)) + for p0, p1 in [(0, 1), (3, 2)] + ] + [ShowCreation(intersection_dot)]) + + name = "LinesIntersectOutside" + for ind in indices: + name += str(ind) + sc.write_to_movie(movie_prefix + name) + +def quadruplets_to_intersections(*radians): + sc = CircleScene(radians) + quadruplets = it.combinations(range(len(radians)), 4) + frame_time = 1.0 + for quad in quadruplets: + intersection_dot = Dot(intersection( + (sc.points[quad[0]], sc.points[quad[2]]), + (sc.points[quad[1]], sc.points[quad[3]]) + )).repeat(3) + dot_quad = [deepcopy(sc.dots[i]) for i in quad] + for dot in dot_quad: + dot.scale_in_place(2) + # arrows = [Arrow(d.get_center()) for d in dot_quad] + dot_quad = CompoundMobject(*dot_quad) + # arrows = CompoundMobject(*arrows) + dot_quad.highlight() + # sc.add(arrows) + sc.add(dot_quad) + sc.dither(frame_time / 3) + sc.animate(Transform( + dot_quad, + intersection_dot, + run_time = 3*frame_time/2 + )) + # sc.remove(arrows) + + name = "QuadrupletsToIntersections" + str(len(radians)) + sc.write_to_movie(movie_prefix + name) + +def defining_graph(graph): + gs = GraphScene(graph) + dots, lines = gs.vertices, gs.edges + gs.remove(*dots + lines) + all_dots = CompoundMobject(*dots) + gs.animate(ShowCreation(all_dots)) + gs.remove(all_dots) + gs.add(*dots) + gs.dither() + gs.animate(*[ + ShowCreation(line) for line in lines + ]) + + #Move to new graph + new_graph = deepcopy(graph) + new_graph["vertices"] = [ + (v[0] + 3*random(), v[1] + 3*random(), 0) + for v in new_graph["vertices"] + ] + ngs = GraphScene(new_graph) + gs.animate(*[ + Transform(m[0], m[1]) + for m in zip(gs.mobjects, ngs.mobjects) + ], run_time = 7.0) + + name = "DefiningGraph" + graph["name"] + gs.write_to_movie(movie_prefix + name) + +def doubled_edges(graph): + gs = GraphScene(graph) + lines_to_double = gs.edges[:9:3] + crazy_lines = [ + ( + line, + Line(line.end, line.start), + CurvedLine(line.start, line.end) , + CurvedLine(line.end, line.start) + ) + for line in lines_to_double + ] + anims = [] + outward_curved_lines = [] + kwargs = {"run_time" : 3.0} + for straight, backwards, inward, outward in crazy_lines: + anims += [ + Transform(straight, inward, **kwargs), + Transform(backwards, outward, **kwargs), + ] + outward_curved_lines.append(outward) + gs.animate(*anims) + gs.dither() + gs.remove(*outward_curved_lines) + + name = "DoubledEdges" + graph["name"] + gs.write_to_movie(movie_prefix + name) + + +def eulers_formula(graph): + gs = GraphScene(graph) + terms = "V - E + F =2".split(" ") + form = dict([ + (key, mob) + for key, mob in zip(terms, tex_mobjects(terms)) + ]) + for mob in form.values(): + mob.shift((0, SPACE_HEIGHT-1.5, 0)) + formula = CompoundMobject(*form.values()) + new_form = dict([ + (key, deepcopy(mob).shift((0, -0.7, 0))) + for key, mob in zip(form.keys(), form.values()) + ]) + gs.add(formula) + colored_dots = [ + deepcopy(d).scale_in_place(1.5).highlight("red") + for d in gs.dots + ] + colored_edges = [ + deepcopy(e).highlight("red") + for e in gs.edges + ] + frame_time = 0.3 + + gs.generate_regions() + parameters = [ + (colored_dots, "V", "mobject", "-", "show_creation"), + (colored_edges, "E", "mobject", "+", "show_creation"), + (gs.regions, "F", "region", "=2", "show_all") + ] + for items, letter, item_type, symbol, mode in parameters: + gs.count( + items, + item_type = item_type, + mode = mode, + num_offset = new_form[letter].get_center(), + run_time = frame_time*len(items) + ) + gs.dither() + if item_type == "mobject": + gs.remove(*items) + gs.add(new_form[symbol]) + gs.reset_background() + + name = "EulersFormula" + graph["name"] + gs.write_to_movie(movie_prefix + name) + + +################################################## + +if __name__ == '__main__': + radians = np.arange(0, 6, 6.0/7) + # count_lines(*radians) + # count_lines(*radians[:4]) + # count_intersection_points(*radians[:4]) + # count_intersection_points(*radians[:6]) + # count_intersection_points(*radians) + # non_general_position() + # line_corresponds_with_pair(radians, radians[3], radians[4]) + # line_corresponds_with_pair(radians, radians[2], radians[5]) + # illustrate_n_choose_k(7, 2) + # illustrate_n_choose_k(6, 4) + # intersection_point_correspondances(radians, range(0, 7, 2)) + # lines_intersect_outside(radians, [2, 4, 5, 6]) + quadruplets_to_intersections(*radians[:6]) + # defining_graph(SAMPLE_GRAPH) + # doubled_edges(CUBE_GRAPH) + # eulers_formula(CUBE_GRAPH) + # eulers_formula(SAMPLE_GRAPH) + # eulers_formula(OCTOHEDRON_GRAPH) + + + + + + + diff --git a/moser/moser_helpers.py b/moser/moser_helpers.py index 0e63e20c..606e796b 100644 --- a/moser/moser_helpers.py +++ b/moser/moser_helpers.py @@ -156,46 +156,41 @@ class PascalsTriangleScene(Scene): def __init__(self, nrows, *args, **kwargs): Scene.__init__(self, *args, **kwargs) - diagram_height = 2*SPACE_HEIGHT - 1 - diagram_width = 1.5*SPACE_WIDTH - cell_height = diagram_height / nrows - cell_width = diagram_width / nrows - portion_to_fill = 0.7 - bottom_left = np.array( - (-cell_width * nrows / 2.0, -cell_height * nrows / 2.0, 0) + self.nrows = nrows + self.diagram_height = 2*SPACE_HEIGHT - 1 + self.diagram_width = 1.5*SPACE_WIDTH + self.cell_height = self.diagram_height / nrows + self.cell_width = self.diagram_width / nrows + self.portion_to_fill = 0.7 + self.bottom_left = np.array( + (-self.cell_width * nrows / 2.0, -self.cell_height * nrows / 2.0, 0) ) num_to_num_mob = {} - coords_to_mobs = {} - coords = [(n, k) for n in range(nrows) for k in range(n+1)] - for n, k in coords: + self.coords_to_mobs = {} + self.coords = [(n, k) for n in range(nrows) for k in range(n+1)] + for n, k in self.coords: num = choose(n, k) - center = bottom_left + ( - cell_width * (k+nrows/2.0 - n/2.0), - cell_height * (nrows - n), - 0 - ) + center = self.coords_to_center(n, k) if num not in num_to_num_mob: num_to_num_mob[num] = tex_mobject(str(num)) num_mob = deepcopy(num_to_num_mob[num]) scale_factor = min( 1, - portion_to_fill * cell_height / num_mob.get_height(), - portion_to_fill * cell_width / num_mob.get_width(), + self.portion_to_fill * self.cell_height / num_mob.get_height(), + self.portion_to_fill * self.cell_width / num_mob.get_width(), ) num_mob.center().scale(scale_factor).shift(center) - if n not in coords_to_mobs: - coords_to_mobs[n] = {} - coords_to_mobs[n][k] = num_mob - self.add(*[coords_to_mobs[n][k] for n, k in coords]) - #Add attributes - self.nrows = nrows - self.coords = coords - self.diagram_height = diagram_height - self.diagram_width = diagram_width - self.cell_height = cell_height - self.cell_width = cell_width - self.portion_to_fill= portion_to_fill - self.coords_to_mobs = coords_to_mobs + if n not in self.coords_to_mobs: + self.coords_to_mobs[n] = {} + self.coords_to_mobs[n][k] = num_mob + self.add(*[self.coords_to_mobs[n][k] for n, k in self.coords]) + + def coords_to_center(self, n, k): + return self.bottom_left + ( + self.cell_width * (k+self.nrows/2.0 - n/2.0), + self.cell_height * (self.nrows - n), + 0 + ) def generate_n_choose_k_mobs(self): self.coords_to_n_choose_k = {} @@ -212,6 +207,18 @@ class PascalsTriangleScene(Scene): self.coords_to_n_choose_k[n] = {} self.coords_to_n_choose_k[n][k] = nck_mob + def generate_sea_of_zeros(self): + zero = tex_mobject("0") + self.sea_of_zeros = [] + for n in range(self.nrows): + for a in range((self.nrows - n)/2 + 1): + for k in (n + a + 1, -a -1): + self.coords.append((n, k)) + mob = deepcopy(zero) + mob.shift(self.coords_to_center(n, k)) + self.coords_to_mobs[n][k] = mob + self.add(mob) + ################################################## diff --git a/script_wrapper.py b/script_wrapper.py index bab2be70..e9e18462 100644 --- a/script_wrapper.py +++ b/script_wrapper.py @@ -86,7 +86,7 @@ def command_line_create_scene(sys_argv, scene_classes, movie_prefix = ""): scene_classes ) name = SceneClass.__name__ + SceneClass.args_to_string(*args) - print "Writing %s..."%name + print "Constructing %s..."%name scene = SceneClass(*args, display_config = display_config) scene.write_to_movie(movie_prefix + name)