diff --git a/animation.py b/animation.py index 5da676e7..1de5902b 100644 --- a/animation.py +++ b/animation.py @@ -263,20 +263,21 @@ class ScaleInPlace(Transform): Transform.__init__(self, mobject, target, *args, **kwargs) class ApplyMethod(Transform): - def __init__(self, method, mobject, *args, **kwargs): + def __init__(self, method, *args, **kwargs): """ - Method is a method of Mobject + Method is a method of Mobject. *args is for the method, + **kwargs is for the transform itself. + + Relies on the fact that mobject methods return the mobject """ - method_args = () - if isinstance(method, tuple): - method, method_args = method[0], method[1:] - if not inspect.ismethod(method): + if not inspect.ismethod(method) or \ + not isinstance(method.im_self, Mobject): raise "Not a valid Mobject method" Transform.__init__( self, - mobject, - method(copy.deepcopy(mobject), *method_args), - *args, **kwargs + method.im_self, + copy.deepcopy(method)(*args), + **kwargs ) class ApplyFunction(Transform): @@ -382,8 +383,28 @@ class Flash(Animation): alpha ) - - +#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 + + def update(self, alpha): + self.start_anim.update(alpha) + self.end_anim.update(alpha) + Transform.update(self, alpha) diff --git a/displayer.py b/displayer.py index 34667d97..dde79c30 100644 --- a/displayer.py +++ b/displayer.py @@ -36,10 +36,12 @@ def paint_mobject(mobject, image_array = None): height = pixels.shape[0] width = pixels.shape[1] space_height = SPACE_HEIGHT - space_width = SPACE_HEIGHT * width / height + space_width = SPACE_HEIGHT * width / height + # camera_distance = 10 - #TODO, Let z add a depth componenet? points = np.array(mobject.points[:, :2]) + # for i in range(2): + # points[:,i] *= camera_distance/(camera_distance-mobject.points[:,2]) rgbs = np.array(mobject.rgbs) #Flips y-axis points[:,1] *= -1 diff --git a/helpers.py b/helpers.py index 4e749fb4..0d66d44e 100644 --- a/helpers.py +++ b/helpers.py @@ -82,6 +82,16 @@ def not_quite_there(t, proportion = 0.7): def wiggle(t, wiggles = 2): return there_and_back(t) * np.sin(wiggles*np.pi*t) +def squish_alpha_func(func, a = 0.4, b = 0.6): + def result(t): + if t < a: + return 0 + elif t > b: + return 1 + else: + return func((t-a)/(b-a)) + return result + ### Functional Functions ### def composition(func_list): diff --git a/image_mobject.py b/image_mobject.py index 4c9341d6..0ac933e7 100644 --- a/image_mobject.py +++ b/image_mobject.py @@ -89,13 +89,13 @@ class VideoIcon(ImageMobject): def text_mobject(text, size = r"\Large"): image = tex_to_image(text, size, TEMPLATE_TEXT_FILE) assert(not isinstance(image, list)) - return ImageMobject(image) + return ImageMobject(image).center() #Purely redundant function to make singulars and plurals sensible -def tex_mobject(expression, size = r"\Large"): +def tex_mobject(expression, size = r"\Huge"): return tex_mobjects(expression, size) -def tex_mobjects(expression, size = r"\Large"): +def tex_mobjects(expression, size = r"\Huge"): images = tex_to_image(expression, size) if isinstance(images, list): #TODO, is checking listiness really the best here? diff --git a/mobject.py b/mobject.py index bcfd9d65..7e283795 100644 --- a/mobject.py +++ b/mobject.py @@ -376,6 +376,49 @@ class Cube(Mobject1D): ]) self.pose_at_angle() +class Octohedron(Mobject1D): + DEFAULT_COLOR = "pink" + def generate_points(self): + x = np.array([1, 0, 0]) + y = np.array([0, 1, 0]) + z = np.array([0, 0, 1]) + vertex_pairs = [(x+y, x-y), (x+y,-x+y), (-x-y,-x+y), (-x-y,x-y)] + vertex_pairs += [ + (b[0]*x+b[1]*y, b[2]*np.sqrt(2)*z) + for b in it.product(*[(-1, 1)]*3) + ] + for pair in vertex_pairs: + self.add_points( + Line(pair[0], pair[1], density = 1/self.epsilon).points + ) + self.pose_at_angle() + +class Dodecahedron(Mobject1D): + DEFAULT_COLOR = "limegreen" + def generate_points(self): + phi = (1 + np.sqrt(5)) / 2 + x = np.array([1, 0, 0]) + y = np.array([0, 1, 0]) + z = np.array([0, 0, 1]) + v1, v2 = (phi, 1/phi, 0), (phi, -1/phi, 0) + vertex_pairs = [ + (v1, v2), + (x+y+z, v1), + (x+y-z, v1), + (x-y+z, v2), + (x-y-z, v2), + ] + five_lines_points = CompoundMobject(*[ + Line(pair[0], pair[1], density = 1.0/self.epsilon) + for pair in vertex_pairs + ]).points + #Rotate those 5 edges into all 30. + for i in range(3): + perm = map(lambda j : j%3, range(i, i+3)) + for b in [-1, 1]: + matrix = b*np.array([x[perm], y[perm], z[perm]]) + self.add_points(np.dot(five_lines_points, matrix)) + self.pose_at_angle() class Sphere(Mobject2D): def generate_points(self): diff --git a/moser/images.py b/moser/images.py new file mode 100644 index 00000000..b1745e5f --- /dev/null +++ b/moser/images.py @@ -0,0 +1,82 @@ +#!/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/main.py b/moser/main.py index 9f01d0cb..b121c063 100644 --- a/moser/main.py +++ b/moser/main.py @@ -168,8 +168,6 @@ class HardProblemsSimplerQuestions(Scene): ) ) - - class CountLines(CircleScene): def __init__(self, radians, *args, **kwargs): CircleScene.__init__(self, radians, *args, **kwargs) @@ -281,6 +279,62 @@ class NonGeneralPosition(CircleScene): for mob1, mob2 in zip(self.mobjects, new_self.mobjects) ]) +class GeneralPositionRule(Scene): + def __init__(self, *args, **kwargs): + Scene.__init__(self, *args, **kwargs) + tuples = [ + ( + np.arange(0, 2*np.pi, np.pi/3), + "Not okay", + zip(range(3), range(3, 6)) + ), + ( + RADIANS, + "Okay", + [], + ), + ( + np.arange(0, 2*np.pi, np.pi/4), + "Not okay", + zip(range(4), range(4, 8)) + ), + ( + [2*np.pi*random() for x in range(5)], + "Okay", + [], + ), + ] + first_time = True + for radians, words, pairs in tuples: + cs = CircleScene(radians) + self.add(*cs.mobjects) + words_mob = text_mobject(words).scale(2).shift((5, 3, 0)) + if not first_time: + self.add(words_mob) + if words == "Okay": + words_mob.highlight("green") + self.dither(2) + else: + words_mob.highlight() + intersecting_lines = [ + line.scale_in_place(0.3).highlight() + for i, j in pairs + for line in [Line(cs.points[i], cs.points[j])] + ] + self.animate(*[ + ShowCreation(line, run_time = 1.0) + for line in intersecting_lines + ]) + if first_time: + self.animate(Transform( + CompoundMobject(*intersecting_lines), + words_mob + )) + first_time = False + self.dither() + self.remove(*self.mobjects) + + class LineCorrespondsWithPair(CircleScene): args_list = [ (RADIANS, 2, 5), @@ -303,7 +357,7 @@ class LineCorrespondsWithPair(CircleScene): self.dots.remove(dot1) self.dither() self.animate(*[ - ApplyMethod((Mobject.fade, 0.2), mob) + ApplyMethod(mob.fade, 0.2) for mob in self.lines + self.dots ]) self.animate(*[ @@ -334,9 +388,9 @@ class IllustrateNChooseK(Scene): [ (r'\\&' if c%(20//k) == 0 else r'\;\;') + str(p) for p, c in zip(tuples, it.count()) - ], - size = r"\small" - ) + ], + size = r"\small", + )#TODO, scale these up! tuple_terms = { 2 : "pairs", 3 : "triplets", @@ -357,9 +411,9 @@ class IllustrateNChooseK(Scene): "%d"%choose(n, k), r" \text{ total %s}"%tuple_term ]) - # pronunciation = text_mobject( - # "(pronounced ``%d choose %d\'\')"%(n, k) - # ) + pronunciation = text_mobject( + "(pronounced ``%d choose %d\'\')"%(n, k) + ) for mob in nrange_mobs: mob.shift((0, 2, 0)) for mob in form1, count, form2: @@ -367,7 +421,9 @@ class IllustrateNChooseK(Scene): count_center = count.get_center() for mob in tuple_mobs: mob.scale(0.6) - # pronunciation.shift(form1.get_center() + (-1, 1, 0)) + pronunciation.shift( + form1.get_center() + (0, 1, 0) + ) self.add(*nrange_mobs) self.dither() @@ -385,7 +441,7 @@ class IllustrateNChooseK(Scene): self.remove(count_mob) self.remove(tuple_copy) self.add(count_mob) - self.animate(FadeIn(CompoundMobject(form1, form2))) + self.animate(FadeIn(CompoundMobject(form1, form2, pronunciation))) class IntersectionPointCorrespondances(CircleScene): args_list = [ @@ -825,12 +881,18 @@ class HowIntersectionChopsLine(CircleScene): ]) -class ApplyEulerToMoser(Scene): +class ApplyEulerToMoser(CircleScene): def __init__(self, *args, **kwargs): - #Boy is this an ugly implementation..., maybe you should - #make a generic formula manipuating module - Scene.__init__(self, *args, **kwargs) - + radius = 2 + CircleScene.__init__(self, *args, radius = radius, **kwargs) + self.generate_intersection_dots() + self.chop_lines_at_intersection_points() + self.chop_circle_at_points() + self.generate_regions() + for dot in self.dots + self.intersection_dots: + dot.scale_in_place(radius / RADIUS) + self.remove(*self.mobjects) + V = {} minus = {} minus1 = {} @@ -864,10 +926,13 @@ class ApplyEulerToMoser(Scene): tex_mobjects(["F", "=", r"{n \choose 2}", "+", r"{n \choose 4}", "+", "2"]) F[7], equals[7], two[7], plus[7], nc2[7], plus1[7], nc4[7] = \ tex_mobjects(["F", "=", "2", "+", r"{n \choose 2}", "+", r"{n \choose 4}"]) + shift_val = (0, 3, 0) for d in dicts: if not d: continue main_key = d.keys()[0] + for key in d.keys(): + d[key].shift(shift_val) main_center = d[main_key].get_center() for key in d.keys(): d[key] = deepcopy(d[main_key]).shift( @@ -879,9 +944,59 @@ class ApplyEulerToMoser(Scene): for d in [V, minus, E, plus, F, equals, two] ]) self.dither() - self.remove(*self.mobjects) + F[1].highlight() + self.add(*self.lines + self.circle_pieces) + for region in self.regions: + self.highlight_region(region) + self.highlight_region(self.exterior, "blue") + self.dither() + self.reset_background() + F[1].highlight("white") + E[1].highlight() + self.remove(*self.lines + self.circle_pieces) + self.animate(*[ + Transform( + deepcopy(line), + deepcopy(line).scale_in_place(0.5), + run_time = 2.0, + ) + for line in self.lines + ] + [ + Transform( + deepcopy(cp), scp, + run_time = 2.0 + ) + for cp, scp in zip(self.circle_pieces, self.smaller_circle_pieces) + ]) + self.dither() + E[1].highlight("white") + V[1].highlight() + self.add(*self.dots + self.intersection_dots) + self.remove(*self.lines + self.circle_pieces) + self.animate(*[ + Transform( + deepcopy(dot), + deepcopy(dot).scale_in_place(1.4).highlight("yellow") + ) + for dot in self.dots + self.intersection_dots + ] + [ + Transform( + deepcopy(line), + deepcopy(line).fade(0.4) + ) + for line in self.lines + self.circle_pieces + ]) + self.dither() + all_mobs = [mob for mob in self.mobjects] + self.remove(*all_mobs) + self.add(*[d[1] for d in [V, minus, E, plus, F, equals, two]]) + V[1].highlight("white") + two[1].highlight() + self.dither() + self.add(*all_mobs) + self.remove(*[d[1] for d in [V, minus, E, plus, F, equals, two]]) self.animate( - Transform(V[2], CompoundMobject(n[3], minus1[3], nc4[3])), + Transform(V[2].repeat(2), CompoundMobject(n[3], minus1[3], nc4[3])), *[ Transform(d[2], d[3]) for d in [F, equals, E, minus, plus, two] @@ -896,7 +1011,17 @@ class ApplyEulerToMoser(Scene): *[ Transform(d[3], d[4]) for d in [F, equals, minus, n, minus1, nc4, plus, two] - ] + ] + [ + Transform( + deepcopy(line), + deepcopy(line).scale_in_place(0.5), + ) + for line in self.lines + ] + [ + Transform(deepcopy(cp), scp) + for cp, scp in zip(self.circle_pieces, self.smaller_circle_pieces) + ], + run_time = 2.0 ) self.dither() self.remove(*self.mobjects) @@ -932,12 +1057,18 @@ class ApplyEulerToMoser(Scene): ] ) self.dither() + self.add(*self.lines + self.circle_pieces) + for region in self.regions: + self.highlight_region(region) + self.dither() + self.highlight_region(self.exterior, "blue") + self.dither() + self.highlight_region(self.exterior, "black") self.remove(two[6]) two = two[7] one = tex_mobject("1").shift(two.get_center()) two.highlight("red") self.add(two) - self.dither() self.animate(SemiCircleTransform(two, one)) class FormulaRelatesToPowersOfTwo(Scene): @@ -1063,7 +1194,7 @@ class PascalsTriangleNChooseKExample(PascalsTriangleScene): [ ShowCreation(mob) for mob in triangle_terms ]+[ - ApplyMethod((Mobject.shift, formula_center), mob) + ApplyMethod(mob.shift, formula_center) for mob in formula_terms ], run_time = 1.0 @@ -1094,7 +1225,7 @@ class PascalsTriangleNChooseKExample(PascalsTriangleScene): self.coords_to_mobs[n][b].highlight("green") self.remove(b_mob) self.animate(*[ - ApplyMethod((Mobject.fade, 0.2), mob) + ApplyMethod(mob.fade, 0.2) for mob in triangle_terms if mob != self.coords_to_mobs[n][k] ]) @@ -1244,6 +1375,214 @@ class MoserSolutionInPascal(PascalsTriangleScene): terms_sum.highlight(term_color) self.animate(Transform(CompoundMobject(*terms), terms_sum)) +class RotatingPolyhedra(Scene): + args_list = [ + ([Cube, Dodecahedron],) + ] + @staticmethod + def args_to_string(class_list): + return "".join([c.__name__ for c in class_list]) + + def __init__(self, polyhedra_classes, *args, **kwargs): + Scene.__init__(self, *args, **kwargs) + rot_kwargs = { + "radians" : np.pi / 2, + "run_time" : 5.0, + "axis" : [0, 1, 0] + } + polyhedra = [ + Class().scale(1.5).shift((1, 0, 0)) + for Class in polyhedra_classes + ] + curr_mob = polyhedra.pop() + for mob in polyhedra: + self.animate(TransformAnimations( + Rotating(curr_mob, **rot_kwargs), + Rotating(mob, **rot_kwargs) + )) + for m in polyhedra: + m.rotate(rot_kwargs["radians"], rot_kwargs["axis"]) + self.animate(Rotating(curr_mob, **rot_kwargs)) + +class ExplainNChoose2Formula(Scene): + args_list = [(7,2,6)] + @staticmethod + def args_to_string(n, a, b): + return "n=%d_a=%d_b=%d"%(n, a, b) + + def __init__(self, n, a, b, *args, **kwargs): + Scene.__init__(self, *args, **kwargs) + r_paren, a_mob, comma, b_mob, l_paren = tex_mobjects( + ("( %d , %d )"%(a, b)).split(" ") + ) + parens = CompoundMobject(r_paren, comma, l_paren) + nums = [tex_mobject(str(k)) for k in range(1, n+1)] + height = 1.5*nums[0].get_height() + for x in range(n): + nums[x].shift((0, x*height, 0)) + nums_compound = CompoundMobject(*nums) + nums_compound.shift(a_mob.get_center() - nums[0].get_center()) + n_mob, n_minus_1, over_2 = tex_mobjects([ + str(n), "(%d-1)"%n, r"\over{2}" + ]) + for part in n_mob, n_minus_1, over_2: + part.shift((SPACE_WIDTH-1.5, SPACE_HEIGHT-1, 0)) + + self.add(parens, n_mob) + up_unit = np.array((0, height, 0)) + self.animate(ApplyMethod(nums_compound.shift, -(n-1)*up_unit)) + self.animate(ApplyMethod(nums_compound.shift, (n-a)*up_unit)) + self.remove(nums_compound) + nums = nums_compound.split() + a_mob = nums.pop(a-1) + nums_compound = CompoundMobject(*nums) + self.add(a_mob, nums_compound) + self.dither() + right_shift = b_mob.get_center() - a_mob.get_center() + right_shift[1] = 0 + self.animate( + ApplyMethod(nums_compound.shift, right_shift), + FadeIn(n_minus_1) + ) + self.animate(ApplyMethod(nums_compound.shift, (a-b)*up_unit)) + self.remove(nums_compound) + nums = nums_compound.split() + b_mob = nums.pop(b-2 if a < b else b-1) + self.add(b_mob) + self.animate(*[ + SemiCircleTransform( + mob, + Point(mob.get_center()).highlight("black") + ) + for mob in nums + ]) + self.animate(*[ + ApplyMethod(mob.shift, (0, 1, 0)) + for mob in parens, a_mob, b_mob + ]) + parens_copy = deepcopy(parens).shift((0, -2, 0)) + a_center = a_mob.get_center() + b_center = b_mob.get_center() + a_copy = deepcopy(a_mob).center().shift(b_center - (0, 2, 0)) + b_copy = deepcopy(b_mob).center().shift(a_center - (0, 2, 0)) + self.add(over_2, deepcopy(a_mob), deepcopy(b_mob)) + self.animate( + SemiCircleTransform(a_mob, a_copy), + SemiCircleTransform(b_mob, b_copy), + FadeIn(parens_copy), + FadeIn(text_mobject("is considered the same as")) + ) + +class ExplainNChoose4Formula(Scene): + args_list = [(7,)] + @staticmethod + def args_to_string(n): + return "n=%d"%n + + def __init__(self, n, *args, **kwargs): + Scene.__init__(self, *args, **kwargs) + # quad = list(it.combinations(range(1,n+1), 4))[randint(0, choose(n, 4)-1)] + quad = (4, 2, 5, 1) + tuple_mobs = tex_mobjects( + ("( %d , %d , %d , %d )"%quad).split(" ") + ) + quad_mobs = tuple_mobs[1::2] + parens = CompoundMobject(*tuple_mobs[0::2]) + form_mobs = tex_mobjects([ + str(n), "(%d-1)"%n, "(%d-2)"%n,"(%d-3)"%n, + r"\over {4 \cdot 3 \cdot 2 \cdot 1}" + ]) + form_mobs = CompoundMobject(*form_mobs).scale(0.7).shift((4, 3, 0)).split() + nums = [tex_mobject(str(k)) for k in range(1, n+1)] + height = 1.5*nums[0].get_height() + for x in range(n): + nums[x].shift((0, x*height, 0)) + nums_compound = CompoundMobject(*nums) + nums_compound.shift(quad_mobs[0].get_center() - nums[0].get_center()) + curr_num = 1 + self.add(parens) + up_unit = np.array((0, height, 0)) + for i in range(4): + self.add(form_mobs[i]) + self.animate(ApplyMethod( + nums_compound.shift, (curr_num-quad[i])*up_unit)) + self.remove(nums_compound) + nums = nums_compound.split() + chosen = nums[quad[i]-1] + nums[quad[i]-1] = Point(chosen.get_center()).highlight("black") + nums_compound = CompoundMobject(*nums) + self.add(chosen) + if i < 3: + right_shift = quad_mobs[i+1].get_center() - chosen.get_center() + right_shift[1] = 0 + self.animate( + ApplyMethod(nums_compound.shift, right_shift) + ) + else: + self.animate(*[ + SemiCircleTransform( + mob, + Point(mob.get_center()).highlight("black") + ) + for mob in nums + ]) + curr_num = quad[i] + self.remove(*self.mobjects) + num_perms_explain = text_mobject( + r"There are $(4 \cdot 3 \cdot 2 \cdot 1)$ total permutations" + ).shift((0, -2, 0)) + self.add(parens, num_perms_explain, *form_mobs) + perms = list(it.permutations(range(4))) + for count in range(6): + perm = perms[randint(0, 23)] + new_quad_mobs = [ + deepcopy(quad_mobs[i]).shift( + quad_mobs[perm[i]].get_center() - \ + quad_mobs[i].get_center() + ) + for i in range(4) + ] + compound_quad = CompoundMobject(*quad_mobs) + self.animate(SemiCircleTransform( + compound_quad, + CompoundMobject(*new_quad_mobs) + )) + self.remove(compound_quad) + quad_mobs = new_quad_mobs + +class IntersectionChoppingExamples(Scene): + def __init__(self, *args, **kwargs): + Scene.__init__(self, *args, **kwargs) + pairs1 = [ + ((-1,-1, 0), (-1, 0, 0)), + ((-1, 0, 0), (-1, 1, 0)), + ((-2, 0, 0), (-1, 0, 0)), + ((-1, 0, 0), ( 1, 0, 0)), + (( 1, 0, 0), ( 2, 0, 0)), + (( 1,-1, 0), ( 1, 0, 0)), + (( 1, 0, 0), ( 1, 1, 0)), + ] + pairs2 = pairs1 + [ + (( 1, 1, 0), ( 1, 2, 0)), + (( 0, 1, 0), ( 1, 1, 0)), + (( 1, 1, 0), ( 2, 1, 0)), + ] + for pairs, exp in [(pairs1, "3 + 2(2) = 7"), + (pairs2, "4 + 2(3) = 10")]: + lines = [Line(*pair).scale(2) for pair in pairs] + self.add(tex_mobject(exp).shift((0, SPACE_HEIGHT-1, 0))) + self.add(*lines) + self.dither() + self.animate(*[ + Transform(line, deepcopy(line).scale(1.2).scale_in_place(1/1.2)) + for line in lines + ]) + self.count(lines, run_time = 3.0, num_offset = ORIGIN) + self.dither() + self.remove(*self.mobjects) + + + ################################################## diff --git a/moser/moser_helpers.py b/moser/moser_helpers.py index 8eb2608f..43342693 100644 --- a/moser/moser_helpers.py +++ b/moser/moser_helpers.py @@ -28,9 +28,9 @@ class CircleScene(Scene): def args_to_string(*args): return str(len(args[0])) #Length of radians - def __init__(self, radians, *args, **kwargs): + def __init__(self, radians, radius = RADIUS, *args, **kwargs): Scene.__init__(self, *args, **kwargs) - self.radius = RADIUS + 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) @@ -39,8 +39,7 @@ class CircleScene(Scene): 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), - size = r"\small" + "n=%d"%len(radians), ).shift((-SPACE_WIDTH+1, SPACE_HEIGHT-1.5, 0)) self.add(self.circle, self.n_equals, *self.dots + self.lines) @@ -110,6 +109,12 @@ class CircleScene(Scene): 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 GraphScene(Scene): args_list = [ (CUBE_GRAPH,),