diff --git a/animation.py b/animation.py index 38e30a5a..5a8e3f82 100644 --- a/animation.py +++ b/animation.py @@ -14,7 +14,7 @@ from constants import * from mobject import Mobject class Animation(object): - def __init__(self, + def __init__(self, mobject, run_time = DEFAULT_ANIMATION_RUN_TIME, alpha_func = high_inflection_0_to_1, @@ -217,6 +217,18 @@ class Transform(Animation): )[self.non_redundant_m2_indices] ) +class FadeToColor(Transform): + def __init__(self, mobject, color, *args, **kwargs): + target = copy.deepcopy(mobject).highlight(color) + Transform.__init__(self, mobject, target, *args, **kwargs) + +class ScaleInPlace(Transform): + def __init__(self, mobject, scale_factor, *args, **kwargs): + target = copy.deepcopy(mobject) + center = mobject.get_center() + target.shift(-center).scale(scale_factor).shift(center) + Transform.__init__(self, mobject, target, *args, **kwargs) + class ApplyMethod(Transform): def __init__(self, method, mobject, *args, **kwargs): """ @@ -232,7 +244,7 @@ class ApplyMethod(Transform): mobject, method(copy.deepcopy(mobject), *method_args), *args, **kwargs - ) + ) class ApplyFunction(Transform): def __init__(self, function, mobject, run_time = DEFAULT_ANIMATION_RUN_TIME, diff --git a/mobject.py b/mobject.py index 237c0a28..812ddcc1 100644 --- a/mobject.py +++ b/mobject.py @@ -130,6 +130,7 @@ class Mobject(object): """ Condition is function which takes in one arguments, (x, y, z). """ + #TODO, Should self.color change? to_change = np.apply_along_axis(condition, 1, self.points) self.rgbs[to_change, :] *= 0 self.rgbs[to_change, :] += Color(color).get_rgb() diff --git a/moser/__init__.py b/moser/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/moser/main.py b/moser/main.py index e37654a4..134d413c 100644 --- a/moser/main.py +++ b/moser/main.py @@ -36,10 +36,17 @@ class CircleScene(Scene): ################################################## def count_lines(*radians): + #TODO, Count things explicitly? sc = CircleScene(radians) - text = tex_mobject(r"\text{How Many Lines?}", size = r"\large") text_center = (sc.radius + 1, sc.radius -1, 0) - text.scale(0.4).shift(text_center) + 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)) @@ -55,6 +62,16 @@ def count_lines(*radians): 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) return sc @@ -63,35 +80,44 @@ def count_intersection_points(*radians): radians.sort() sc = CircleScene(radians) intersection_points = [ - intersection([p[0], p[2]], [p[1], p[3]]) + intersection((p[0], p[2]), (p[1], p[3])) for p in it.combinations(sc.points, 4) ] - intersection_dots = CompoundMobject(*[ - Dot(point) for point in intersection_points - ]) - how_many = tex_mobject(r""" - \text{How many}\\ - \text{intersection points?} - """, size = r"\large") - text_center = (sc.radius + 1, sc.radius -1, 0) - how_many.scale(0.4).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 + intersection_dots = [Dot(point) for point in intersection_points] + text_center = (sc.radius + 1, 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(how_many) - sc.animate(ShowCreation(intersection_dots)) - sc.add(intersection_dots) - sc.animate(Transform(intersection_dots, new_dots)) - sc.add(tex_mobject(str(len(new_points))).center()) + sc.add(text) + sc.count(intersection_dots, "show_creation", 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(FadeIn(formula)) #Put here to so they are foreground + sc.animate(*anims, run_time = 1) return sc def non_general_position(): @@ -127,11 +153,52 @@ def non_general_position(): ]) return sc1 +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) 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)) + return sc + +def illustrate_n_choose_2(n): + #TODO, maybe make this snazzy + sc = Scene() + nrange = range(1, n+1) + nrange_im = tex_mobject(str(nrange)) + pairs_str = str(list(it.combinations(nrange, 2))) + exp = tex_mobject(r"{{%d \choose 2} = %d \text{ total pairs}}"%(n, choose(n, 2))) + pairs_im = tex_mobject(r"\underbrace{%s}"%pairs_str, size=r"\tiny") + nrange_im.shift((0, 2, 0)) + pairs_im.scale(0.7) + exp.shift((0, -2, 0)) + sc.add(nrange_im) + sc.dither() + sc.animate(FadeIn(pairs_im), FadeIn(exp)) + sc.add(pairs_im) + return sc + ################################################## if __name__ == '__main__': radians = np.arange(0, 6, 6.0/7) - count_lines(*radians).write_to_movie("moser/CountLines") + # count_lines(*radians).write_to_movie("moser/CountLines") count_intersection_points(*radians).write_to_movie("moser/CountIntersectionPoints") - non_general_position().write_to_movie("moser/NonGeneralPosition") + # non_general_position().write_to_movie("moser/NonGeneralPosition") + # line_corresponds_with_pair(radians, radians[3], radians[4]).write_to_movie("moser/LineCorrespondsWithPair34") + # line_corresponds_with_pair(radians, radians[2], radians[5]).write_to_movie("moser/LineCorrespondsWithPair25") + # illustrate_n_choose_2(6).write_to_movie("moser/IllustrateNChoose2with6") diff --git a/moser/moser_helpers.py b/moser/moser_helpers.py index e1ba9b6f..c3fad5d0 100644 --- a/moser/moser_helpers.py +++ b/moser/moser_helpers.py @@ -2,6 +2,9 @@ import numpy as np import operator as op import itertools as it +from constants import * +from image_mobject import * + def choose(n, r): if n < r: return 0 if r == 0: return 1 @@ -32,3 +35,6 @@ def intersection(line1, line2): result = np.dot(transform, [[x_intercept], [0]]) result = result.reshape((2,)) + p0 return result + + + diff --git a/scene.py b/scene.py index 1ea1f4cb..8a9919fd 100644 --- a/scene.py +++ b/scene.py @@ -12,6 +12,7 @@ import inspect from helpers import * from mobject import * +from image_mobject import * from animation import * import displayer as disp @@ -98,6 +99,37 @@ class Scene(object): self.add(*moving_mobjects) progress_bar.finish() + def count(self, mobjects, mode = "highlight", + color = "red", + num_offset = (SPACE_WIDTH - 1, SPACE_HEIGHT - 1, 0), + run_time = 5.0): + """ + Note: Leaves scene with a "number" attribute + for the final number mobject + """ + if len(mobjects) > 50: #TODO + raise Exception("I don't know if you should be counting \ + too many mobjects...") + if mode not in ["highlight", "show_creation"]: + raise Exception("Invalid mode") + frame_time = run_time / len(mobjects) + if mode == "highlight": + self.add(*mobjects) + for mob, num in zip(mobjects, it.count(1)): + num_mob = tex_mobject(str(num)) + num_mob.center().shift(num_offset) + self.add(num_mob) + if mode == "highlight": + original_color = mob.color + mob.highlight(color) + self.dither(frame_time) + mob.highlight(original_color) + if mode == "show_creation": + self.animate(ShowCreation(mob, run_time = frame_time)) + self.remove(num_mob) + self.add(num_mob) + self.number = num_mob + def get_frame(self): frame = self.background for mob in self.mobjects: