From c84b26ccce700ec25dcca8e5c6a43fc80969f00c Mon Sep 17 00:00:00 2001 From: Sridhar Ramesh Date: Tue, 20 Feb 2018 21:54:40 -0800 Subject: [PATCH 1/7] Incremental --- active_projects/WindingNumber.py | 74 +++++++++++++++++++++++++------- camera/camera.py | 4 +- helpers.py | 4 +- 3 files changed, 63 insertions(+), 19 deletions(-) diff --git a/active_projects/WindingNumber.py b/active_projects/WindingNumber.py index 6181aa39..19288680 100644 --- a/active_projects/WindingNumber.py +++ b/active_projects/WindingNumber.py @@ -540,7 +540,7 @@ class ColorMappedByFuncScene(Scene): self.pos_to_color_func = self.func # func_hash hashes the function at some random points - func_hash_points = [(-0.93, 1), (1, -2.7), (20, 4)] + func_hash_points = [(-0.93, 1), (1.3, -2.7), (20.5, 4)] to_hash = tuple((self.func(p)[0], self.func(p)[1]) for p in func_hash_points) func_hash = hash(to_hash) full_hash = hash((func_hash, self.camera.pixel_shape)) @@ -681,6 +681,14 @@ class EquationSolver2d(ColorMappedByFuncScene): rev_func = lambda p : point_to_rev(self.func(p)) clockwise_rev_func = lambda p : -rev_func(p) + base_line = Line(UP, RIGHT, stroke_width = 10 if self.use_fancy_lines else 4, color = RED) + + run_time_base = 1 + run_time_with_lingering = run_time_base + 0.2 + base_rate = lambda t : t + linger_rate = squish_rate_func(lambda t : t, 0, + fdiv(run_time_base, run_time_with_lingering)) + def Animate2dSolver(cur_depth, rect, dim_to_split, sides_to_draw = [0, 1, 2, 3]): print "Solver at depth: " + str(cur_depth) @@ -691,13 +699,10 @@ class EquationSolver2d(ColorMappedByFuncScene): alpha_winder = make_alpha_winder(clockwise_rev_func, start, end, self.num_checkpoints) a0 = alpha_winder(0) rebased_winder = lambda alpha: alpha_winder(alpha) - a0 + start_wind - thick_line = Line(num_plane.coords_to_point(*start), num_plane.coords_to_point(*end), - stroke_width = 10, - color = RED) + colored_line = Line(num_plane.coords_to_point(*start), num_plane.coords_to_point(*end)) + colored_line.match_style(base_line) if self.use_fancy_lines: - colored_line = thick_line.color_using_background_image(self.background_image_file) - else: - colored_line = thick_line.set_stroke(width = 4) + colored_line.color_using_background_image(self.background_image_file) walker_anim = LinearWalker( start_coords = start, @@ -707,17 +712,20 @@ class EquationSolver2d(ColorMappedByFuncScene): number_update_func = rebased_winder, remover = True ) - + if should_linger: # Do we need an "and not self.display_in_parallel" here? - rate_func = lingering + run_time = run_time_with_lingering + rate_func = linger_rate else: - rate_func = None + run_time = run_time_base + rate_func = base_rate opt_line_anim = ShowCreation(colored_line) if draw_line else empty_animation line_draw_anim = AnimationGroup( opt_line_anim, walker_anim, + run_time = run_time, rate_func = rate_func) return (line_draw_anim, rebased_winder(1)) @@ -795,11 +803,41 @@ class EquationSolver2d(ColorMappedByFuncScene): cur_depth = 0, rect = rect, dim_to_split = 0, + sides_to_draw = [] ) print "Done computing anim" - self.play(anim) + # Keep timing details here in sync with details above + rect_points = [ + rect.get_top_left(), + rect.get_top_right(), + rect.get_bottom_right(), + rect.get_bottom_left(), + ] + border = Polygon(*map(lambda x : num_plane.coords_to_point(*x), rect_points)) + border.match_style(base_line) + if self.use_fancy_lines: + border.color_using_background_image(self.background_image_file) + + rect_time_without_linger = 4 * run_time_base + rect_time_with_linger = 3 * run_time_base + run_time_with_lingering + def rect_rate(alpha): + time_in = alpha * rect_time_with_linger + if time_in < 3 * run_time_base: + return fdiv(time_in, 4 * run_time_base) + else: + time_in_last_leg = time_in - 3 * run_time_base + alpha_in_last_leg = fdiv(time_in_last_leg, run_time_with_lingering) + return interpolate(0.75, 1, linger_rate(alpha_in_last_leg)) + + border_anim = ShowCreation( + border, + run_time = rect_time_with_linger, + rate_func = rect_rate + ) + + self.play(anim, border_anim) self.wait() @@ -1011,12 +1049,18 @@ class SignsExplanation(Scene): num_line.number_to_point(neg_num), buff = 0, color = negative_color) + + plus_sign = TexMobject("+", fill_color = positive_color) + minus_sign = TexMobject("-", fill_color = negative_color) + + plus_sign.next_to(pos_arrow, UP) + minus_sign.next_to(neg_arrow, UP) #num_line.add_numbers(pos_num) - self.play(ShowCreation(pos_arrow)) + self.play(ShowCreation(pos_arrow), FadeIn(plus_sign)) #num_line.add_numbers(neg_num) - self.play(ShowCreation(neg_arrow)) + self.play(ShowCreation(neg_arrow), FadeIn(minus_sign)) class VectorField(Scene): CONFIG = { @@ -1362,9 +1406,9 @@ class LoopSplitSceneMapped(LoopSplitScene): class FundThmAlg(EquationSolver2d): CONFIG = { "func" : plane_poly_with_roots((1, 2), (-1, 1.5), (-1, 1.5)), - "num_iterations" : 5, + "num_iterations" : 1, "display_in_parallel" : True, - "use_fancy_lines" : False, + "use_fancy_lines" : True, } # TODO: Borsuk-Ulam visuals diff --git a/camera/camera.py b/camera/camera.py index 2943162c..50608f7c 100644 --- a/camera/camera.py +++ b/camera/camera.py @@ -433,12 +433,12 @@ class Camera(object): # When the output alpha is 0 for full transparency, # we have a choice over what RGB value to use in our - # output representation. We choose 0.0 here. + # output representation. We choose 0 here. out_rgb = fdiv( src_rgb*src_a[..., None] + \ dst_rgb*dst_a[..., None]*(1.0-src_a[..., None]), out_a[..., None], - zero_over_zero_value = 0.0 + zero_over_zero_value = 0 ) self.pixel_array[..., :3] = out_rgb*self.rgb_max_val diff --git a/helpers.py b/helpers.py index 878eb4e9..9eddc42e 100644 --- a/helpers.py +++ b/helpers.py @@ -696,8 +696,8 @@ class DictAsObject(object): # Just to have a less heavyweight name for this extremely common operation # # We may wish to have more fine-grained control over division by zero behavior -# in future (separate specifiable default values for 0/0 and x/0 with x != 0), -# but for now, we just allow the option to handle 0/0. +# in the future (separate specifiable values for 0/0 and x/0 with x != 0), +# but for now, we just allow the option to handle indeterminate 0/0. def fdiv(a, b, zero_over_zero_value = None): if zero_over_zero_value != None: out = np.full_like(a, zero_over_zero_value) From 9927f226453161431a8309cd53f5d33382208ab2 Mon Sep 17 00:00:00 2001 From: Sridhar Ramesh Date: Wed, 21 Feb 2018 17:11:53 -0800 Subject: [PATCH 2/7] Fixed small bug in Arc, where end and start were swapped in adding tips --- topics/geometry.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/topics/geometry.py b/topics/geometry.py index ec1b3f73..64b3bce1 100644 --- a/topics/geometry.py +++ b/topics/geometry.py @@ -45,7 +45,7 @@ class Arc(VMobject): #TODO, do this a better way p1 = p2 = p3 = p4 = None start_arrow = end_arrow = None - if at_start: + if at_end: p1, p2 = self.points[-3:-1] # self.points[-2:] did overshoot start_arrow = Arrow( @@ -55,7 +55,7 @@ class Arc(VMobject): ) self.add(start_arrow.split()[-1]) # just the tip - if at_end: + if at_start: p4, p3 = self.points[1:3] # self.points[:2] did overshoot end_arrow = Arrow( From c036cfe8239658969c6b05ceffcaf794dbc011a8 Mon Sep 17 00:00:00 2001 From: Sridhar Ramesh Date: Wed, 21 Feb 2018 17:12:23 -0800 Subject: [PATCH 3/7] Allowed color arg to be interpreted as setting fill_color in TexMobjects --- mobject/tex_mobject.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mobject/tex_mobject.py b/mobject/tex_mobject.py index 650362f0..7900e794 100644 --- a/mobject/tex_mobject.py +++ b/mobject/tex_mobject.py @@ -41,6 +41,10 @@ class TexMobject(SVGMobject): } def __init__(self, *args, **kwargs): digest_config(self, kwargs, locals()) + + if "color" in kwargs.keys() and not "fill_color" in kwargs.keys(): + self.fill_color = kwargs["color"] + ##TODO, Eventually remove this if len(args) == 1 and isinstance(args[0], list): self.args = args[0] From cf8b25bcc0162a5ceb6892e82c0c2094f8a6c3b0 Mon Sep 17 00:00:00 2001 From: Sridhar Ramesh Date: Wed, 21 Feb 2018 17:12:45 -0800 Subject: [PATCH 4/7] Incremental --- active_projects/WindingNumber.py | 91 ++++++++++++++++++++++++++++---- 1 file changed, 80 insertions(+), 11 deletions(-) diff --git a/active_projects/WindingNumber.py b/active_projects/WindingNumber.py index 19288680..33240571 100644 --- a/active_projects/WindingNumber.py +++ b/active_projects/WindingNumber.py @@ -444,9 +444,10 @@ class WalkerAnimation(Animation): self.show_arrows = show_arrows base_walker = Dot().scale(5) # PiCreature().scale(0.8) # - self.compound_walker.walker = base_walker.scale(0.35).set_stroke(BLACK, 1.5) #PiCreature() + self.compound_walker.walker = base_walker.scale(0.35).set_stroke(WHITE, 3) #PiCreature() if show_arrows: - self.compound_walker.arrow = Arrow(ORIGIN, 0.5 * RIGHT, buff = 0).set_stroke(BLACK, 1.5) + self.compound_walker.arrow = Arrow(ORIGIN, 0.5 * RIGHT, buff = 0) + self.compound_walker.arrow.match_style(self.compound_walker.walker) self.compound_walker.digest_mobject_attrs() Animation.__init__(self, self.compound_walker, **kwargs) @@ -670,6 +671,8 @@ class EquationSolver2d(ColorMappedByFuncScene): } def construct(self): + print "Z order enabled?", self.camera.use_z_coordinate_for_display_order + ColorMappedByFuncScene.construct(self) num_plane = self.num_plane self.remove(num_plane) @@ -699,7 +702,7 @@ class EquationSolver2d(ColorMappedByFuncScene): alpha_winder = make_alpha_winder(clockwise_rev_func, start, end, self.num_checkpoints) a0 = alpha_winder(0) rebased_winder = lambda alpha: alpha_winder(alpha) - a0 + start_wind - colored_line = Line(num_plane.coords_to_point(*start), num_plane.coords_to_point(*end)) + colored_line = Line(num_plane.coords_to_point(*start) + IN, num_plane.coords_to_point(*end) + IN) colored_line.match_style(base_line) if self.use_fancy_lines: colored_line.color_using_background_image(self.background_image_file) @@ -752,7 +755,7 @@ class EquationSolver2d(ColorMappedByFuncScene): rect.get_bottom_right(), rect.get_bottom_left() ] - points = np.array([num_plane.coords_to_point(x, y) for (x, y) in coords]) + 2 * IN + points = np.array([num_plane.coords_to_point(x, y) for (x, y) in coords]) + 3 * IN # TODO: Maybe use diagonal lines or something to fill in rectangles indicating # their "Nothing here" status? # Or draw a large X or something @@ -774,7 +777,7 @@ class EquationSolver2d(ColorMappedByFuncScene): for (sub_rect, side_to_draw) in sub_rect_and_sides ] mid_line_coords = rect.split_line_on_dim(dim_to_split) - mid_line_points = [num_plane.coords_to_point(x, y) + IN for (x, y) in mid_line_coords] + mid_line_points = [num_plane.coords_to_point(x, y) + 2 * IN for (x, y) in mid_line_coords] # TODO: Have this match rectangle line style, apart from dashes and thin-ness? # Though there is also informational value in seeing the dashed line separately from rectangle lines mid_line = DashedLine(*mid_line_points) @@ -815,7 +818,7 @@ class EquationSolver2d(ColorMappedByFuncScene): rect.get_bottom_right(), rect.get_bottom_left(), ] - border = Polygon(*map(lambda x : num_plane.coords_to_point(*x), rect_points)) + border = Polygon(*map(lambda x : num_plane.coords_to_point(*x) + IN, rect_points)) border.match_style(base_line) if self.use_fancy_lines: border.color_using_background_image(self.background_image_file) @@ -999,8 +1002,8 @@ class SecondSqrtScene(FirstSqrtScene): class RewriteEquation(Scene): def construct(self): - # Can maybe fitz around with smoothening the transform, so just = goes to - and new stuff - # is added at the right end, while things re-center + # Can maybe use get_center() to perfectly center Groups before and after transform + f_old = TexMobject("f(x)") f_new = f_old.copy() equals_old = TexMobject("=") @@ -1016,8 +1019,18 @@ class RewriteEquation(Scene): f_new.next_to(minus_new, LEFT) equals_new.next_to(g_new, RIGHT) zero_new.next_to(equals_new, RIGHT) + + # where_old = TextMobject("Where does ") + # where_old.next_to(f_old, LEFT) + # where_new = where_old.copy() + # where_new.next_to(f_new, LEFT) - self.add(f_old, equals_old, equals_old_2, g_old) + # qmark_old = TextMobject("?") + # qmark_old.next_to(g_old, RIGHT) + # qmark_new = qmark_old.copy() + # qmark_new.next_to(zero_new, RIGHT) + + self.add(f_old, equals_old, equals_old_2, g_old) #, where_old, qmark_old) self.wait() self.play( ReplacementTransform(f_old, f_new), @@ -1025,6 +1038,8 @@ class RewriteEquation(Scene): ReplacementTransform(g_old, g_new), ReplacementTransform(equals_old_2, minus_new), ShowCreation(zero_new), + # ReplacementTransform(where_old, where_new), + # ReplacementTransform(qmark_old, qmark_new), ) self.wait() @@ -1101,6 +1116,7 @@ class VectorField(Scene): self.wait() class HasItsLimitations(Scene): + def construct(self): num_line = NumberLine() num_line.add_numbers() @@ -1108,17 +1124,70 @@ class HasItsLimitations(Scene): self.wait() + base_point = num_line.number_to_point(3) + OUT + + dot_color = ORANGE + + input_dot = Dot(base_point, color = dot_color) + input_label = TexMobject("Input", fill_color = dot_color) + input_label.next_to(input_dot, UP + LEFT) + input_label.add_background_rectangle() + self.add(input_dot, input_label) + + curved_arrow = Arc(0, color = MAROON_E) + curved_arrow.set_bound_angles(np.pi, 0) + curved_arrow.generate_points() + curved_arrow.add_tip() + curved_arrow.move_arc_center_to(base_point + RIGHT) + self.play(ShowCreation(curved_arrow)) + + output_dot = Dot(base_point + 2 * RIGHT, color = dot_color) + output_label = TexMobject("Output", fill_color = dot_color) + output_label.next_to(output_dot, UP + RIGHT) + output_label.add_background_rectangle() + + self.add(output_dot, output_label) + self.wait() + num_plane = NumberPlane() num_plane.add_coordinates() - self.play(FadeOut(num_line), FadeIn(num_plane)) + new_base_point = base_point + 2 * UP + new_input_dot = input_dot.copy().move_to(new_base_point) + new_input_label = input_label.copy().next_to(new_input_dot, UP + LEFT) + + new_curved_arrow = Arc(0).match_style(curved_arrow) + new_curved_arrow.set_bound_angles(np.pi * 3/4, 0) + new_curved_arrow.generate_points() + new_curved_arrow.add_tip() + + input_diff = input_dot.get_center() - curved_arrow.points[0] + output_diff = output_dot.get_center() - curved_arrow.points[-1] + + new_curved_arrow.shift((new_input_dot.get_center() - new_curved_arrow.points[0]) - input_diff) + + new_output_dot = output_dot.copy().move_to(new_curved_arrow.points[-1] + output_diff) + new_output_label = output_label.copy().next_to(new_output_dot, UP + RIGHT) + + dot_objects = Group(input_dot, input_label, output_dot, output_label, curved_arrow) + new_dot_objects = Group(new_input_dot, new_input_label, new_output_dot, new_output_label, new_curved_arrow) + + self.play( + FadeOut(num_line), FadeIn(num_plane), + ReplacementTransform(dot_objects, new_dot_objects), + ) self.wait() + self.add_foreground_mobject(new_dot_objects) + complex_plane = ComplexPlane() complex_plane.add_coordinates() + # This looks a little wonky and we may wish to do a crossfade in Premiere instead self.play(FadeOut(num_plane), FadeIn(complex_plane)) + + self.wait() class ComplexPlaneIs2d(Scene): @@ -1406,7 +1475,7 @@ class LoopSplitSceneMapped(LoopSplitScene): class FundThmAlg(EquationSolver2d): CONFIG = { "func" : plane_poly_with_roots((1, 2), (-1, 1.5), (-1, 1.5)), - "num_iterations" : 1, + "num_iterations" : 2, "display_in_parallel" : True, "use_fancy_lines" : True, } From 6365de0b81077915b343b54aafa9ca23e1fa39f5 Mon Sep 17 00:00:00 2001 From: Sridhar Ramesh Date: Thu, 22 Feb 2018 12:05:57 -0800 Subject: [PATCH 5/7] Incremental --- active_projects/WindingNumber.py | 87 +++++++++++++++++++++++++------- animation/simple_animations.py | 15 +++--- 2 files changed, 77 insertions(+), 25 deletions(-) diff --git a/active_projects/WindingNumber.py b/active_projects/WindingNumber.py index 33240571..9198a9c1 100644 --- a/active_projects/WindingNumber.py +++ b/active_projects/WindingNumber.py @@ -33,6 +33,9 @@ from topics.graph_scene import * # (and it will be done, but first I'll figure out what I'm doing with all this...) # -SR +# This turns counterclockwise revs into their color. Beware, we use CCW angles +# in all display code, but generally think in this video's script in terms of +# CW angles def rev_to_rgba(alpha): alpha = (0.5 - alpha) % 1 # For convenience, to go CW from red on left instead of CCW from right # 0 is red, 1/6 is yellow, 1/3 is green, 2/3 is blue @@ -76,7 +79,7 @@ def point_to_rgba(point): rgba = rev_to_rgba(rev) base_size = np.sqrt(point[0]**2 + point[1]**2) rescaled_size = np.sqrt(base_size/(base_size + 1)) - return rgba * rescaled_size + return rgba * [rescaled_size, rescaled_size, rescaled_size, 1] # Preserve alpha positive_color = rev_to_color(0) negative_color = rev_to_color(0.5) @@ -541,8 +544,16 @@ class ColorMappedByFuncScene(Scene): self.pos_to_color_func = self.func # func_hash hashes the function at some random points - func_hash_points = [(-0.93, 1), (1.3, -2.7), (20.5, 4)] - to_hash = tuple((self.func(p)[0], self.func(p)[1]) for p in func_hash_points) + jitter_val = 0.1 + line_coords = np.linspace(-10, 10) + jitter_val + func_hash_points = it.product(line_coords, line_coords) + def mini_hasher(p): + func_val = self.func(p) + color_func_val = point_to_rgba(p) + if color_func_val[3] != 1.0: + print "Warning! point_to_rgba assigns fractional alpha", color_func_val[3] + return tuple(func_val), tuple(color_func_val) + to_hash = tuple(mini_hasher(p) for p in func_hash_points) func_hash = hash(to_hash) full_hash = hash((func_hash, self.camera.pixel_shape)) self.background_image_file = os.path.join( @@ -584,11 +595,23 @@ class PureColorMap(ColorMappedByFuncScene): "show_num_plane" : False } -class ColorMappedByFuncStill(ColorMappedByFuncScene): def construct(self): ColorMappedByFuncScene.construct(self) self.wait() +# This sets self.background_image_file, but does not display it as the background +class ColorMappedObjectsScene(ColorMappedByFuncScene): + CONFIG = { + "show_num_plane" : False + } + + def construct(self): + ColorMappedByFuncScene.construct(self) + + # Clearing background + self.camera.background_image = None + self.camera.init_background() + class PiWalker(ColorMappedByFuncScene): CONFIG = { "walk_coords" : [], @@ -655,7 +678,7 @@ class PiWalkerCircle(PiWalker): PiWalker.setup(self) # TODO: Give drawn lines a bit of buffer, so that the rectangle's corners are filled in -class EquationSolver2d(ColorMappedByFuncScene): +class EquationSolver2d(ColorMappedObjectsScene): CONFIG = { "camera_config" : {"use_z_coordinate_for_display_order": True}, "initial_lower_x" : -5.1, @@ -671,15 +694,8 @@ class EquationSolver2d(ColorMappedByFuncScene): } def construct(self): - print "Z order enabled?", self.camera.use_z_coordinate_for_display_order - - ColorMappedByFuncScene.construct(self) + ColorMappedObjectsScene.construct(self) num_plane = self.num_plane - self.remove(num_plane) - - # Clearing background - self.camera.background_image = None - self.camera.init_background() rev_func = lambda p : point_to_rev(self.func(p)) clockwise_rev_func = lambda p : -rev_func(p) @@ -906,10 +922,9 @@ class FuncRotater(Animation): def update_mobject(self, alpha): Animation.update_mobject(self, alpha) - angle_revs = self.rotate_func(alpha) - # We do a clockwise rotation + angle_revs = -self.rotate_func(alpha) # Negated so we interpret this clockwise self.mobject.rotate( - -angle_revs * TAU, + angle_revs * TAU, about_point = ORIGIN ) self.mobject.set_color(rev_to_color(angle_revs)) @@ -924,8 +939,9 @@ class TestRotater(Scene): # TODO: Be careful about clockwise vs. counterclockwise convention throughout! # Make sure this is correct everywhere in resulting video. -class OdometerScene(Scene): +class OdometerScene(ColorMappedObjectsScene): CONFIG = { + # "func" : lambda p : 100 * p # Full coloring, essentially "rotate_func" : lambda x : np.sin(x * TAU), "run_time" : 5, "dashed_line_angle" : None, @@ -933,8 +949,11 @@ class OdometerScene(Scene): } def construct(self): + ColorMappedObjectsScene.construct(self) + radius = 1.3 circle = Circle(center = ORIGIN, radius = radius) + circle.color_using_background_image(self.background_image_file) self.add(circle) if self.dashed_line_angle: @@ -943,9 +962,13 @@ class OdometerScene(Scene): dashed_line.rotate(-self.dashed_line_angle * TAU, about_point = ORIGIN) self.add(dashed_line) - num_display = DecimalNumber(0, include_background_rectangle = False).set_stroke(1) + num_display = DecimalNumber(0, include_background_rectangle = False) num_display.move_to(2 * DOWN) + caption = TextMobject("turns clockwise") + caption.next_to(num_display, DOWN) + self.add(caption) + display_val_bias = 0 if self.biased_display_start != None: display_val_bias = self.biased_display_start - self.rotate_func(0) @@ -1139,6 +1162,7 @@ class HasItsLimitations(Scene): curved_arrow.generate_points() curved_arrow.add_tip() curved_arrow.move_arc_center_to(base_point + RIGHT) + # Could do something smoother, with arrowhead moving along partial arc? self.play(ShowCreation(curved_arrow)) output_dot = Dot(base_point + 2 * RIGHT, color = dot_color) @@ -1326,6 +1350,33 @@ class Initial2dFuncSceneWithoutMorphing(Scene): line = Line(points[i], points[i + 1], color = RED) self.play(ShowCreation(line)) +class DemonstrateColorMapping(ColorMappedObjectsScene): + CONFIG = { + "show_num_plane" : False + } + + def construct(self): + ColorMappedObjectsScene.construct(self) + + circle = Circle() + circle.color_using_background_image(self.background_image_file) + + ray = Line(ORIGIN, 10 * RIGHT) + ray.color_using_background_image(self.background_image_file) + + self.play(ShowCreation(circle)) + + self.play(ShowCreation(ray)) + + scale_factor = 5 + self.play(ApplyMethod(circle.scale, scale_factor)) + + self.play(ApplyMethod(circle.scale, fdiv(1, scale_factor**2))) + + self.play(ApplyMethod(circle.scale, scale_factor)) + + self.play(Rotating(ray, about_point = ORIGIN)) + # TODO: Illustrations for introducing domain coloring # TODO: Bunch of Pi walker scenes diff --git a/animation/simple_animations.py b/animation/simple_animations.py index 978209c7..c489391d 100644 --- a/animation/simple_animations.py +++ b/animation/simple_animations.py @@ -464,21 +464,22 @@ class Succession(Animation): self.current_alpha = alpha return - gt_alpha_list = filter( + gt_alpha_iter = it.ifilter( lambda i : self.critical_alphas[i+1] >= alpha, - range(len(self.critical_alphas)-1) + range(self.num_anims) ) - if gt_alpha_list: - i = gt_alpha_list[0] - else: + i = next(gt_alpha_iter, None) + if i == None: + # In this case, we assume what is happening is that alpha is 1.0, + # but that rounding error is causing us to overshoot the end of + # self.critical_alphas (which is also 1.0) if not abs(alpha - 1) < 0.001: warnings.warn( "Rounding error not near alpha=1 in Succession.update_mobject," + \ "instead alpha = %f"%alpha ) print self.critical_alphas, alpha - i = len(self.critical_alphas) - 2 - # + i = self.num_anims - 1 # At this point, we should have self.critical_alphas[i] <= alpha <= self.critical_alphas[i +1] From 2966c1c31e503fa131a9427f785094ee32effd6a Mon Sep 17 00:00:00 2001 From: Sridhar Ramesh Date: Thu, 22 Feb 2018 15:32:49 -0800 Subject: [PATCH 6/7] Incremental --- active_projects/WindingNumber.py | 51 +++++++++++++++++++++++++++----- 1 file changed, 43 insertions(+), 8 deletions(-) diff --git a/active_projects/WindingNumber.py b/active_projects/WindingNumber.py index 9198a9c1..92eb5759 100644 --- a/active_projects/WindingNumber.py +++ b/active_projects/WindingNumber.py @@ -427,6 +427,12 @@ def plane_func_from_complex_func(f): def point_func_from_complex_func(f): return lambda (x, y, z): complex_to_R3(f(complex(x, y))) +def point_func_from_plane_func(f): + def g((x, y, z)): + f_val = f((x, y)) + return np.array((f_val[0], f_val[1], 0)) + return g + test_map_func = point_func_from_complex_func(lambda c: c**2) empty_animation = EmptyAnimation() @@ -532,10 +538,14 @@ class ColorMappedByFuncScene(Scene): "num_plane" : NumberPlane(), "show_num_plane" : True, - "show_output" : False, # Not currently implemented; TODO + "show_output" : False } def setup(self): + # The composition of input_to_pos and pos_to_color + # is to be equal to func (which turns inputs into colors) + # However, depending on whether we are showing input or output (via a MappingCamera), + # we color the background using either func or the identity map if self.show_output: self.input_to_pos_func = self.func self.pos_to_color_func = lambda p : p @@ -543,18 +553,19 @@ class ColorMappedByFuncScene(Scene): self.input_to_pos_func = lambda p : p self.pos_to_color_func = self.func - # func_hash hashes the function at some random points jitter_val = 0.1 line_coords = np.linspace(-10, 10) + jitter_val func_hash_points = it.product(line_coords, line_coords) def mini_hasher(p): - func_val = self.func(p) - color_func_val = point_to_rgba(p) - if color_func_val[3] != 1.0: - print "Warning! point_to_rgba assigns fractional alpha", color_func_val[3] - return tuple(func_val), tuple(color_func_val) + rgba = point_to_rgba(self.pos_to_color_func(p)) + if rgba[3] != 1.0: + print "Warning! point_to_rgba assigns fractional alpha", rgba[3] + return tuple(rgba) to_hash = tuple(mini_hasher(p) for p in func_hash_points) func_hash = hash(to_hash) + # We hash just based on output image + # Thus, multiple scenes with same output image can re-use it + # without recomputation full_hash = hash((func_hash, self.camera.pixel_shape)) self.background_image_file = os.path.join( self.output_directory, "images", @@ -1648,6 +1659,30 @@ class CombineInterval2(Scene): self.wait() +tiny_loop_func = plane_poly_with_roots((-1, -2), (1, 1), (1, 1)) +class TinyLoopInInputPlane(ColorMappedByFuncScene): + CONFIG = { + "func" : tiny_loop_func + } + + def construct(self): + ColorMappedByFuncScene.construct(self) + self.wait() + + circle = Circle(color = WHITE) + circle.scale(0.5) + circle.move_to(UP + RIGHT) + + self.play(ShowCreation(circle)) + +class TinyLoopInOutputPlane(TinyLoopInInputPlane): + CONFIG = { + "camera_class" : MappingCamera, + "camera_config" : {"mapping_func" : point_func_from_plane_func(tiny_loop_func)}, + "show_output" : True, + "show_num_plane" : False, + } + # TODO: Brouwer's fixed point theorem visuals # class BFTScene(Scene): @@ -1691,7 +1726,7 @@ class MapPiWalkerRect(PiWalkerRect): CONFIG = { "camera_class" : MappingCamera, "camera_config" : {"mapping_func" : rect_to_circle}, - "display_output_color_map" : True + "show_output" : True } class ShowBack(PiWalkerRect): From 1b9cde00d95d1c75d65e831160f7e5fff9a01fca Mon Sep 17 00:00:00 2001 From: Sridhar Ramesh Date: Mon, 26 Feb 2018 11:47:37 -0800 Subject: [PATCH 7/7] Minor fix to camera.py, making SplitScreenCameras work again after some change to background handling made them crash --- camera/camera.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/camera/camera.py b/camera/camera.py index e4703ead..ef988f89 100644 --- a/camera/camera.py +++ b/camera/camera.py @@ -107,7 +107,7 @@ class Camera(object): def set_pixel_array(self, pixel_array, convert_from_floats = False): converted_array = self.convert_pixel_array(pixel_array, convert_from_floats) - if not hasattr(self, "pixel_array"): #TODO: And the shapes match? + if not (hasattr(self, "pixel_array") and self.pixel_array.shape == converted_array.shape): self.pixel_array = converted_array else: #Set in place