From e8d9f6d6e5cccd04da0882e1c6d2053d9d4bf996 Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Wed, 21 Feb 2018 18:27:50 +0100 Subject: [PATCH 01/11] incremental changes in basel --- active_projects/basel.py | 409 ++++----------------------------------- 1 file changed, 38 insertions(+), 371 deletions(-) diff --git a/active_projects/basel.py b/active_projects/basel.py index 2e468e50..b46ed878 100644 --- a/active_projects/basel.py +++ b/active_projects/basel.py @@ -197,16 +197,13 @@ class ScaleLightSources(Transform): new_sp.scale(factor,about_point = about_point) submob.move_source_to(new_sp.get_location()) - #ambient_of = copy_func(submob.ambient_light.opacity_function) - #new_of = lambda r: ambient_of(r/factor) - #submob.ambient_light.opacity_function = new_of + ambient_of = copy_func(submob.ambient_light.opacity_function) + new_of = lambda r: ambient_of(r/factor) + submob.ambient_light.opacity_function = new_of - #spotlight_of = copy_func(submob.ambient_light.opacity_function) - #new_of = lambda r: spotlight_of(r/factor) - #submob.spotlight.change_opacity_function(new_of) - - new_r = factor * submob.radius - submob.set_radius(new_r) + spotlight_of = copy_func(submob.ambient_light.opacity_function) + new_of = lambda r: spotlight_of(r/factor) + submob.spotlight.change_opacity_function(new_of) new_r = factor * submob.ambient_light.radius submob.ambient_light.radius = new_r @@ -1004,7 +1001,6 @@ class ScreenShapingScene(ThreeDScene): def construct(self): - #self.force_skipping() self.setup_elements() self.deform_screen() self.create_brightness_rect() @@ -1014,10 +1010,8 @@ class ScreenShapingScene(ThreeDScene): self.add_distance_arrow() self.right_shift_screen_while_showing_light_indicator_and_distance_arrow() self.left_shift_again() - #self.revert_to_original_skipping_status() self.morph_into_3d() - self.prove_inverse_square_law() def setup_elements(self): @@ -1033,14 +1027,11 @@ class ScreenShapingScene(ThreeDScene): # light source self.light_source = LightSource( - opacity_function = inverse_quadratic(1,5,1), + opacity_function = inverse_quadratic(1,2,1), num_levels = NUM_LEVELS, radius = 10, - max_opacity = 0.2 #screen = self.screen ) - self.light_source.set_max_opacity_spotlight(0.2) - self.light_source.set_screen(self.screen) self.light_source.move_source_to([-5,0,0]) @@ -1257,9 +1248,6 @@ class ScreenShapingScene(ThreeDScene): def morph_into_3d(self): - - self.play(FadeOut(self.morty)) - axes = ThreeDAxes() self.add(axes) @@ -1267,6 +1255,11 @@ class ScreenShapingScene(ThreeDScene): theta0 = self.camera.get_theta() # default is -90 degs distance0 = self.camera.get_distance() + # this is an ugly hack bc remove, FadeOut and SwitchOff don't work + self.play( + self.light_source.set_max_opacity_ambient,0.001 + ) + phi1 = 60 * DEGREES # angle from zenith (0 to 180) theta1 = -135 * DEGREES # azimuth (0 to 360) distance1 = distance0 @@ -1279,7 +1272,7 @@ class ScreenShapingScene(ThreeDScene): projection_direction = self.camera.spherical_coords_to_point(phi1,theta1, 1) new_screen0 = Rectangle(height = self.screen_height, - width = 0.1, stroke_color = RED, fill_color = RED, fill_opacity = 1) + width = 0.1, stroke_color = RED) new_screen0.rotate(TAU/4,axis = DOWN) new_screen0.move_to(self.screen.get_center()) self.add(new_screen0) @@ -1289,199 +1282,19 @@ class ScreenShapingScene(ThreeDScene): self.light_source.set_camera(self.camera) - new_screen = Rectangle(height = self.screen_height, - width = self.screen_height, stroke_color = RED, fill_color = RED, fill_opacity = 1) - new_screen.rotate(TAU/4,axis = DOWN) - new_screen.move_to(self.screen.get_center()) + new_screen = new_screen0.deepcopy() + new_screen.width = new_screen.height - self.add_foreground_mobject(self.ambient_light) - self.add_foreground_mobject(self.spotlight) - self.add_foreground_mobject(self.light_source.shadow) self.play( ApplyMethod(self.camera.rotation_mobject.move_to, camera_target_point), ) - self.remove(self.spotlight) - self.play(Transform(new_screen0,new_screen)) + #self.play(Transform(new_screen0,new_screen)) self.wait() - self.unit_screen = new_screen0 # better name - - - - def prove_inverse_square_law(self): - - def orientate(mob): - mob.move_to(self.unit_screen) - mob.rotate(TAU/4, axis = LEFT) - mob.rotate(TAU/4, axis = OUT) - mob.rotate(TAU/2, axis = LEFT) - return mob - - unit_screen_copy = self.unit_screen.copy() - fourfold_screen = self.unit_screen.copy() - fourfold_screen.scale(2,about_point = self.light_source.get_source_point()) - - self.remove(self.spotlight) - - - reading1 = TexMobject("1") - orientate(reading1) - - self.play(FadeIn(reading1)) - self.wait() - self.play(FadeOut(reading1)) - - - self.play( - Transform(self.unit_screen, fourfold_screen) - ) - - reading21 = TexMobject("{1\over 4}").scale(0.8) - orientate(reading21) - reading22 = reading21.deepcopy() - reading23 = reading21.deepcopy() - reading24 = reading21.deepcopy() - reading21.shift(0.5*OUT + 0.5*UP) - reading22.shift(0.5*OUT + 0.5*DOWN) - reading23.shift(0.5*IN + 0.5*UP) - reading24.shift(0.5*IN + 0.5*DOWN) - - - corners = fourfold_screen.get_anchors() - midpoint1 = (corners[0] + corners[1])/2 - midpoint2 = (corners[1] + corners[2])/2 - midpoint3 = (corners[2] + corners[3])/2 - midpoint4 = (corners[3] + corners[0])/2 - midline1 = Line(midpoint1, midpoint3) - midline2 = Line(midpoint2, midpoint4) - - self.play( - ShowCreation(midline1), - ShowCreation(midline2) - ) - - self.play( - FadeIn(reading21), - FadeIn(reading22), - FadeIn(reading23), - FadeIn(reading24), - ) - - self.wait() - - self.play( - FadeOut(reading21), - FadeOut(reading22), - FadeOut(reading23), - FadeOut(reading24), - FadeOut(midline1), - FadeOut(midline2) - ) - - ninefold_screen = unit_screen_copy.copy() - ninefold_screen.scale(3,about_point = self.light_source.get_source_point()) - - self.play( - Transform(self.unit_screen, ninefold_screen) - ) - - reading31 = TexMobject("{1\over 9}").scale(0.8) - orientate(reading31) - reading32 = reading31.deepcopy() - reading33 = reading31.deepcopy() - reading34 = reading31.deepcopy() - reading35 = reading31.deepcopy() - reading36 = reading31.deepcopy() - reading37 = reading31.deepcopy() - reading38 = reading31.deepcopy() - reading39 = reading31.deepcopy() - reading31.shift(IN + UP) - reading32.shift(IN) - reading33.shift(IN + DOWN) - reading34.shift(UP) - reading35.shift(ORIGIN) - reading36.shift(DOWN) - reading37.shift(OUT + UP) - reading38.shift(OUT) - reading39.shift(OUT + DOWN) - - corners = ninefold_screen.get_anchors() - midpoint11 = (2*corners[0] + corners[1])/3 - midpoint12 = (corners[0] + 2*corners[1])/3 - midpoint21 = (2*corners[1] + corners[2])/3 - midpoint22 = (corners[1] + 2*corners[2])/3 - midpoint31 = (2*corners[2] + corners[3])/3 - midpoint32 = (corners[2] + 2*corners[3])/3 - midpoint41 = (2*corners[3] + corners[0])/3 - midpoint42 = (corners[3] + 2*corners[0])/3 - midline11 = Line(midpoint11, midpoint32) - midline12 = Line(midpoint12, midpoint31) - midline21 = Line(midpoint21, midpoint42) - midline22 = Line(midpoint22, midpoint41) - - self.play( - ShowCreation(midline11), - ShowCreation(midline12), - ShowCreation(midline21), - ShowCreation(midline22), - ) - - self.play( - FadeIn(reading31), - FadeIn(reading32), - FadeIn(reading33), - FadeIn(reading34), - FadeIn(reading35), - FadeIn(reading36), - FadeIn(reading37), - FadeIn(reading38), - FadeIn(reading39), - ) - - - - -class IndicatorScalingScene(Scene): - - def construct(self): - - unit_intensity = 0.6 - - indicator1 = LightIndicator(show_reading = False, color = LIGHT_COLOR) - indicator1.set_intensity(unit_intensity) - reading1 = TexMobject("1") - reading1.move_to(indicator1) - - - indicator2 = LightIndicator(show_reading = False, color = LIGHT_COLOR) - indicator2.shift(2*RIGHT) - indicator2.set_intensity(unit_intensity/4) - reading2 = TexMobject("{1\over 4}").scale(0.8) - reading2.move_to(indicator2) - - indicator3 = LightIndicator(show_reading = False, color = LIGHT_COLOR) - indicator3.shift(4*RIGHT) - indicator3.set_intensity(unit_intensity/9) - reading3 = TexMobject("{1\over 9}").scale(0.8) - reading3.move_to(indicator3) - - - self.play(FadeIn(indicator1)) - self.play(FadeIn(reading1)) - self.wait() - self.play(FadeOut(reading1)) - self.play(Transform(indicator1, indicator2)) - self.play(FadeIn(reading2)) - self.wait() - self.play(FadeOut(reading2)) - self.play(Transform(indicator1, indicator3)) - self.play(FadeIn(reading3)) - self.wait() - @@ -2301,7 +2114,7 @@ class PondScene(Scene): return position - def split_light_source(i, step, show_steps = True, run_time = 1, ls_radius = 1): + def split_light_source(i, step, show_steps = True, run_time = 1): ls_new_loc1 = position_for_index(i,step + 1) ls_new_loc2 = position_for_index(i + 2**step,step + 1) @@ -2333,28 +2146,17 @@ class PondScene(Scene): self.add(ls2) self.additional_light_sources.append(ls2) - # check if the light sources are on screen - ls_old_loc = np.array(ls1.get_source_point()) - onscreen_old = np.all(np.abs(ls_old_loc) < 10) - onscreen_1 = np.all(np.abs(ls_new_loc1) < 10) - onscreen_2 = np.all(np.abs(ls_new_loc2) < 10) - show_animation = (onscreen_old or onscreen_1 or onscreen_2) - - if show_animation: - self.play( - ApplyMethod(ls1.move_source_to,ls_new_loc1, run_time = run_time), - ApplyMethod(ls2.move_source_to,ls_new_loc2, run_time = run_time), - ) - else: - ls1.move_source_to(ls_new_loc1) - ls2.move_source_to(ls_new_loc1) + self.play( + ApplyMethod(ls1.move_source_to,ls_new_loc1, run_time = run_time), + ApplyMethod(ls2.move_source_to,ls_new_loc2, run_time = run_time), + ) def construction_step(n, scale_down = True, show_steps = True, run_time = 1, - simultaneous_splitting = False, ls_radius = 1): + simultaneous_splitting = False): # we assume that the scene contains: # an inner lake, self.inner_lake @@ -2415,12 +2217,7 @@ class PondScene(Scene): self.new_hypotenuses = [] for i in range(2**n): - split_light_source(i, - step = n, - show_steps = show_steps, - run_time = run_time, - ls_radius = ls_radius - ) + split_light_source(i, step = n, show_steps = show_steps, run_time = run_time) @@ -2482,12 +2279,6 @@ class PondScene(Scene): self.outer_lake.scale_about_point,0.5,OBSERVER_POINT, ) - # update the radii bc they haven't done so themselves - # bc reasons... - for ls in self.light_sources_array: - r = ls.radius - ls.set_radius(r*0.5) - else: # update the lake center and the radius self.lake_center = ls0_loc = self.outer_lake.get_center() + self.lake_radius * UP @@ -2528,12 +2319,8 @@ class PondScene(Scene): self.new_legs_2 = [] self.new_hypotenuses = [] - ls_radius = 25.0 - for i in range(3): - construction_step(i, scale_down = True, ls_radius = ls_radius/2**i) - - return + construction_step(i, scale_down = True) self.play( FadeOut(self.altitudes), @@ -2541,141 +2328,21 @@ class PondScene(Scene): FadeOut(self.legs) ) - for i in range(3,5): + for i in range(3,10): construction_step(i, scale_down = False, show_steps = False, run_time = 1.0/2**i, - simultaneous_splitting = True, ls_radius = ls_radius/2**3) - - - - - # Now create a straight number line and transform into it - MAX_N = 17 - - self.number_line = NumberLine( - x_min = -MAX_N, - x_max = MAX_N + 1, - color = WHITE, - number_at_center = 0, - stroke_width = LAKE_STROKE_WIDTH, - stroke_color = LAKE_STROKE_COLOR, - numbers_with_elongated_ticks = range(-MAX_N,MAX_N + 1), - numbers_to_show = range(-MAX_N,MAX_N + 1), - unit_size = LAKE0_RADIUS * TAU/4 / 4, - tick_frequency = 1, - line_to_number_buff = LARGE_BUFF, - label_direction = UP, - ).shift(2.5 * DOWN) - - self.number_line.label_direction = DOWN - - self.number_line_labels = self.number_line.get_number_mobjects() - self.wait() - - origin_point = self.number_line.number_to_point(0) - nl_sources = VMobject() - pond_sources = VMobject() - - for i in range(-MAX_N,MAX_N+1): - anchor = self.number_line.number_to_point(2*i + 1) - ls = self.light_sources_array[i].copy() - ls.move_source_to(anchor) - nl_sources.add(ls) - pond_sources.add(self.light_sources_array[i].copy()) - - self.add(pond_sources) - self.remove(self.light_sources) - - self.outer_lake.rotate(TAU/8) - - # open sea - open_sea = Rectangle( - width = 20, - height = 10, - stroke_width = LAKE_STROKE_WIDTH, - stroke_color = LAKE_STROKE_COLOR, - fill_color = LAKE_COLOR, - fill_opacity = LAKE_OPACITY, - ).flip().next_to(origin_point,UP,buff = 0) - - - - self.play( - Transform(pond_sources,nl_sources), - Transform(self.outer_lake,open_sea), - FadeOut(self.inner_lake) - ) - self.play(FadeIn(self.number_line)) - - - -class LabeledArc(Arc): - CONFIG = { - "length" : 1 - } - - def __init__(self, angle, **kwargs): - - BUFFER = 1.3 - - Arc.__init__(self,angle,**kwargs) - - label = DecimalNumber(self.length, num_decimal_points = 0) - r = BUFFER * self.radius - theta = self.start_angle + self.angle/2 - label_pos = r * np.array([np.cos(theta), np.sin(theta), 0]) - - label.move_to(label_pos) - self.add(label) - - - - -class ArcHighlightOverlayScene(Scene): - - def construct(self): - - BASELINE_YPOS = -2.5 - OBSERVER_POINT = [0,BASELINE_YPOS,0] - LAKE0_RADIUS = 1.5 - INDICATOR_RADIUS = 0.6 - TICK_SIZE = 0.5 - LIGHTHOUSE_HEIGHT = 0.2 - LAKE_COLOR = BLUE - LAKE_OPACITY = 0.15 - LAKE_STROKE_WIDTH = 5.0 - LAKE_STROKE_COLOR = BLUE - TEX_SCALE = 0.8 - DOT_COLOR = BLUE - - FLASH_TIME = 0.25 - - def flash_arcs(n): - - angle = TAU/2**n - arcs = [] - arcs.append(LabeledArc(angle/2, start_angle = -TAU/4, radius = LAKE0_RADIUS, length = 1)) - - for i in range(1,2**n): - arcs.append(LabeledArc(angle, start_angle = -TAU/4 + (i-0.5)*angle, radius = LAKE0_RADIUS, length = 2)) - - arcs.append(LabeledArc(angle/2, start_angle = -TAU/4 - angle/2, radius = LAKE0_RADIUS, length = 1)) - - self.play( - FadeIn(arcs[0], run_time = FLASH_TIME) - ) - - for i in range(1,2**n + 1): - self.play( - FadeOut(arcs[i-1], run_time = FLASH_TIME), - FadeIn(arcs[i], run_time = FLASH_TIME) - ) - - self.play( - FadeOut(arcs[2**n], run_time = FLASH_TIME), - ) - - - flash_arcs(3) + simultaneous_splitting = True) + + + + + + + + + + + + From 831257b68aeb2a94f5d031f9090909682751f975 Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Wed, 21 Feb 2018 19:09:22 +0100 Subject: [PATCH 02/11] day's work --- active_projects/basel.py | 507 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 470 insertions(+), 37 deletions(-) diff --git a/active_projects/basel.py b/active_projects/basel.py index b46ed878..7eb9285a 100644 --- a/active_projects/basel.py +++ b/active_projects/basel.py @@ -197,13 +197,16 @@ class ScaleLightSources(Transform): new_sp.scale(factor,about_point = about_point) submob.move_source_to(new_sp.get_location()) - ambient_of = copy_func(submob.ambient_light.opacity_function) - new_of = lambda r: ambient_of(r/factor) - submob.ambient_light.opacity_function = new_of + # ambient_of = copy_func(submob.ambient_light.opacity_function) + # new_of = lambda r: ambient_of(r / factor) + # submob.ambient_light.change_opacity_function(new_of) - spotlight_of = copy_func(submob.ambient_light.opacity_function) - new_of = lambda r: spotlight_of(r/factor) - submob.spotlight.change_opacity_function(new_of) + # spotlight_of = copy_func(submob.ambient_light.opacity_function) + # new_of = lambda r: spotlight_of(r / factor) + # submob.spotlight.change_opacity_function(new_of) + + new_r = factor * submob.radius + submob.set_radius(new_r) new_r = factor * submob.ambient_light.radius submob.ambient_light.radius = new_r @@ -1001,6 +1004,7 @@ class ScreenShapingScene(ThreeDScene): def construct(self): + #self.force_skipping() self.setup_elements() self.deform_screen() self.create_brightness_rect() @@ -1010,8 +1014,10 @@ class ScreenShapingScene(ThreeDScene): self.add_distance_arrow() self.right_shift_screen_while_showing_light_indicator_and_distance_arrow() self.left_shift_again() + #self.revert_to_original_skipping_status() self.morph_into_3d() + self.prove_inverse_square_law() def setup_elements(self): @@ -1027,11 +1033,14 @@ class ScreenShapingScene(ThreeDScene): # light source self.light_source = LightSource( - opacity_function = inverse_quadratic(1,2,1), + opacity_function = inverse_quadratic(1,5,1), num_levels = NUM_LEVELS, radius = 10, + max_opacity = 0.2 #screen = self.screen ) + self.light_source.set_max_opacity_spotlight(0.2) + self.light_source.set_screen(self.screen) self.light_source.move_source_to([-5,0,0]) @@ -1248,6 +1257,9 @@ class ScreenShapingScene(ThreeDScene): def morph_into_3d(self): + + self.play(FadeOut(self.morty)) + axes = ThreeDAxes() self.add(axes) @@ -1255,11 +1267,6 @@ class ScreenShapingScene(ThreeDScene): theta0 = self.camera.get_theta() # default is -90 degs distance0 = self.camera.get_distance() - # this is an ugly hack bc remove, FadeOut and SwitchOff don't work - self.play( - self.light_source.set_max_opacity_ambient,0.001 - ) - phi1 = 60 * DEGREES # angle from zenith (0 to 180) theta1 = -135 * DEGREES # azimuth (0 to 360) distance1 = distance0 @@ -1272,7 +1279,7 @@ class ScreenShapingScene(ThreeDScene): projection_direction = self.camera.spherical_coords_to_point(phi1,theta1, 1) new_screen0 = Rectangle(height = self.screen_height, - width = 0.1, stroke_color = RED) + width = 0.1, stroke_color = RED, fill_color = RED, fill_opacity = 1) new_screen0.rotate(TAU/4,axis = DOWN) new_screen0.move_to(self.screen.get_center()) self.add(new_screen0) @@ -1282,19 +1289,199 @@ class ScreenShapingScene(ThreeDScene): self.light_source.set_camera(self.camera) - new_screen = new_screen0.deepcopy() - new_screen.width = new_screen.height + new_screen = Rectangle(height = self.screen_height, + width = self.screen_height, stroke_color = RED, fill_color = RED, fill_opacity = 1) + new_screen.rotate(TAU/4,axis = DOWN) + new_screen.move_to(self.screen.get_center()) + self.add_foreground_mobject(self.ambient_light) + self.add_foreground_mobject(self.spotlight) + self.add_foreground_mobject(self.light_source.shadow) self.play( ApplyMethod(self.camera.rotation_mobject.move_to, camera_target_point), ) + self.remove(self.spotlight) - #self.play(Transform(new_screen0,new_screen)) + self.play(Transform(new_screen0,new_screen)) self.wait() + self.unit_screen = new_screen0 # better name + + + + def prove_inverse_square_law(self): + + def orientate(mob): + mob.move_to(self.unit_screen) + mob.rotate(TAU/4, axis = LEFT) + mob.rotate(TAU/4, axis = OUT) + mob.rotate(TAU/2, axis = LEFT) + return mob + + unit_screen_copy = self.unit_screen.copy() + fourfold_screen = self.unit_screen.copy() + fourfold_screen.scale(2,about_point = self.light_source.get_source_point()) + + self.remove(self.spotlight) + + + reading1 = TexMobject("1") + orientate(reading1) + + self.play(FadeIn(reading1)) + self.wait() + self.play(FadeOut(reading1)) + + + self.play( + Transform(self.unit_screen, fourfold_screen) + ) + + reading21 = TexMobject("{1\over 4}").scale(0.8) + orientate(reading21) + reading22 = reading21.deepcopy() + reading23 = reading21.deepcopy() + reading24 = reading21.deepcopy() + reading21.shift(0.5*OUT + 0.5*UP) + reading22.shift(0.5*OUT + 0.5*DOWN) + reading23.shift(0.5*IN + 0.5*UP) + reading24.shift(0.5*IN + 0.5*DOWN) + + + corners = fourfold_screen.get_anchors() + midpoint1 = (corners[0] + corners[1])/2 + midpoint2 = (corners[1] + corners[2])/2 + midpoint3 = (corners[2] + corners[3])/2 + midpoint4 = (corners[3] + corners[0])/2 + midline1 = Line(midpoint1, midpoint3) + midline2 = Line(midpoint2, midpoint4) + + self.play( + ShowCreation(midline1), + ShowCreation(midline2) + ) + + self.play( + FadeIn(reading21), + FadeIn(reading22), + FadeIn(reading23), + FadeIn(reading24), + ) + + self.wait() + + self.play( + FadeOut(reading21), + FadeOut(reading22), + FadeOut(reading23), + FadeOut(reading24), + FadeOut(midline1), + FadeOut(midline2) + ) + + ninefold_screen = unit_screen_copy.copy() + ninefold_screen.scale(3,about_point = self.light_source.get_source_point()) + + self.play( + Transform(self.unit_screen, ninefold_screen) + ) + + reading31 = TexMobject("{1\over 9}").scale(0.8) + orientate(reading31) + reading32 = reading31.deepcopy() + reading33 = reading31.deepcopy() + reading34 = reading31.deepcopy() + reading35 = reading31.deepcopy() + reading36 = reading31.deepcopy() + reading37 = reading31.deepcopy() + reading38 = reading31.deepcopy() + reading39 = reading31.deepcopy() + reading31.shift(IN + UP) + reading32.shift(IN) + reading33.shift(IN + DOWN) + reading34.shift(UP) + reading35.shift(ORIGIN) + reading36.shift(DOWN) + reading37.shift(OUT + UP) + reading38.shift(OUT) + reading39.shift(OUT + DOWN) + + corners = ninefold_screen.get_anchors() + midpoint11 = (2*corners[0] + corners[1])/3 + midpoint12 = (corners[0] + 2*corners[1])/3 + midpoint21 = (2*corners[1] + corners[2])/3 + midpoint22 = (corners[1] + 2*corners[2])/3 + midpoint31 = (2*corners[2] + corners[3])/3 + midpoint32 = (corners[2] + 2*corners[3])/3 + midpoint41 = (2*corners[3] + corners[0])/3 + midpoint42 = (corners[3] + 2*corners[0])/3 + midline11 = Line(midpoint11, midpoint32) + midline12 = Line(midpoint12, midpoint31) + midline21 = Line(midpoint21, midpoint42) + midline22 = Line(midpoint22, midpoint41) + + self.play( + ShowCreation(midline11), + ShowCreation(midline12), + ShowCreation(midline21), + ShowCreation(midline22), + ) + + self.play( + FadeIn(reading31), + FadeIn(reading32), + FadeIn(reading33), + FadeIn(reading34), + FadeIn(reading35), + FadeIn(reading36), + FadeIn(reading37), + FadeIn(reading38), + FadeIn(reading39), + ) + + + + +class IndicatorScalingScene(Scene): + + def construct(self): + + unit_intensity = 0.6 + + indicator1 = LightIndicator(show_reading = False, color = LIGHT_COLOR) + indicator1.set_intensity(unit_intensity) + reading1 = TexMobject("1") + reading1.move_to(indicator1) + + + indicator2 = LightIndicator(show_reading = False, color = LIGHT_COLOR) + indicator2.shift(2*RIGHT) + indicator2.set_intensity(unit_intensity/4) + reading2 = TexMobject("{1\over 4}").scale(0.8) + reading2.move_to(indicator2) + + indicator3 = LightIndicator(show_reading = False, color = LIGHT_COLOR) + indicator3.shift(4*RIGHT) + indicator3.set_intensity(unit_intensity/9) + reading3 = TexMobject("{1\over 9}").scale(0.8) + reading3.move_to(indicator3) + + + self.play(FadeIn(indicator1)) + self.play(FadeIn(reading1)) + self.wait() + self.play(FadeOut(reading1)) + self.play(Transform(indicator1, indicator2)) + self.play(FadeIn(reading2)) + self.wait() + self.play(FadeOut(reading2)) + self.play(Transform(indicator1, indicator3)) + self.play(FadeIn(reading3)) + self.wait() + @@ -1962,6 +2149,10 @@ class PondScene(Scene): TEX_SCALE = 0.8 DOT_COLOR = BLUE + LIGHT_MAX_INT = 1 + LIGHT_SCALE = 5 + LIGHT_CUTOFF = 1 + baseline = VMobject() baseline.set_points_as_corners([[-8,BASELINE_YPOS,0],[8,BASELINE_YPOS,0]]) @@ -1988,7 +2179,8 @@ class PondScene(Scene): indicator.next_to(morty,LEFT) # first lighthouse - ls0 = LightSource() + original_op_func = inverse_quadratic(LIGHT_MAX_INT,LIGHT_SCALE,LIGHT_CUTOFF) + ls0 = LightSource(opacity_function = original_op_func) ls0.move_source_to(OBSERVER_POINT + LAKE0_RADIUS * 2 * UP) self.add(lake0,morty,obs_dot,ls0_dot, ls0.lighthouse) @@ -2055,11 +2247,11 @@ class PondScene(Scene): FadeOut(ls0_dot) ) - indicator.reading = TexMobject("{1\over d^2}").scale(TEX_SCALE) - indicator.reading.move_to(indicator) + indicator_reading = TexMobject("{1\over d^2}").scale(TEX_SCALE) + indicator_reading.move_to(indicator) self.play( - FadeIn(indicator.reading) + FadeIn(indicator_reading) ) # replace d with its value @@ -2075,7 +2267,7 @@ class PondScene(Scene): new_reading.move_to(indicator) self.play( - Transform(indicator.reading,new_reading) + Transform(indicator_reading,new_reading) ) self.play( @@ -2094,7 +2286,7 @@ class PondScene(Scene): self.play( ScaleInPlace(indicator, INDICATOR_WIGGLE_FACTOR, rate_func = wiggle), - ScaleInPlace(indicator.reading, INDICATOR_WIGGLE_FACTOR, rate_func = wiggle) + ScaleInPlace(indicator_reading, INDICATOR_WIGGLE_FACTOR, rate_func = wiggle) ) @@ -2114,7 +2306,7 @@ class PondScene(Scene): return position - def split_light_source(i, step, show_steps = True, run_time = 1): + def split_light_source(i, step, show_steps = True, run_time = 1, scale_down = True): ls_new_loc1 = position_for_index(i,step + 1) ls_new_loc2 = position_for_index(i + 2**step,step + 1) @@ -2142,14 +2334,43 @@ class PondScene(Scene): ) ls1 = self.light_sources_array[i] + + if scale_down: + # print "scaling down" + # #new_of = lambda x: original_op_func(x/0.5**step) + # #new_of = inverse_quadratic(LIGHT_MAX_INT, LIGHT_SCALE * 10**(step+1), LIGHT_CUTOFF) + # print "==============" + # for t in np.arange(0.0,5.0,1.0): + # print ls1.ambient_light.opacity_function(t) + + # print "==============" + + new_of = lambda x: np.sin(10*x) + ls1.ambient_light.change_opacity_function(lambda x: np.sin(100*x)) + # for t in np.arange(0.0,5.0,1.0): + # print ls1.ambient_light.opacity_function(t) + + ls1.spotlight.change_opacity_function(new_of) + ls2 = ls1.copy() self.add(ls2) self.additional_light_sources.append(ls2) - self.play( - ApplyMethod(ls1.move_source_to,ls_new_loc1, run_time = run_time), - ApplyMethod(ls2.move_source_to,ls_new_loc2, run_time = run_time), - ) + # check if the light sources are on screen + ls_old_loc = np.array(ls1.get_source_point()) + onscreen_old = np.all(np.abs(ls_old_loc) < 10) + onscreen_1 = np.all(np.abs(ls_new_loc1) < 10) + onscreen_2 = np.all(np.abs(ls_new_loc2) < 10) + show_animation = (onscreen_old or onscreen_1 or onscreen_2) + + if show_animation: + self.play( + ApplyMethod(ls1.move_source_to,ls_new_loc1, run_time = run_time), + ApplyMethod(ls2.move_source_to,ls_new_loc2, run_time = run_time), + ) + else: + ls1.move_source_to(ls_new_loc1) + ls2.move_source_to(ls_new_loc1) @@ -2217,7 +2438,12 @@ class PondScene(Scene): self.new_hypotenuses = [] for i in range(2**n): - split_light_source(i, step = n, show_steps = show_steps, run_time = run_time) + split_light_source(i, + step = n, + show_steps = show_steps, + run_time = run_time, + scale_down = scale_down + ) @@ -2279,6 +2505,12 @@ class PondScene(Scene): self.outer_lake.scale_about_point,0.5,OBSERVER_POINT, ) + # update the radii bc they haven't done so themselves + # bc reasons... + for ls in self.light_sources_array: + r = ls.radius + ls.set_radius(r*0.5) + else: # update the lake center and the radius self.lake_center = ls0_loc = self.outer_lake.get_center() + self.lake_radius * UP @@ -2319,30 +2551,231 @@ class PondScene(Scene): self.new_legs_2 = [] self.new_hypotenuses = [] + ls_radius = 25.0 + for i in range(3): construction_step(i, scale_down = True) + + self.play( FadeOut(self.altitudes), FadeOut(self.hypotenuses), FadeOut(self.legs) ) - for i in range(3,10): + for i in range(3,5): construction_step(i, scale_down = False, show_steps = False, run_time = 1.0/2**i, simultaneous_splitting = True) - - - - - - - - + # Now create a straight number line and transform into it + MAX_N = 17 + + self.number_line = NumberLine( + x_min = -MAX_N, + x_max = MAX_N + 1, + color = WHITE, + number_at_center = 0, + stroke_width = LAKE_STROKE_WIDTH, + stroke_color = LAKE_STROKE_COLOR, + #numbers_with_elongated_ticks = range(-MAX_N,MAX_N + 1), + numbers_to_show = range(-MAX_N,MAX_N + 1,2), + unit_size = LAKE0_RADIUS * TAU/4 / 4, + tick_frequency = 1, + line_to_number_buff = LARGE_BUFF, + label_direction = UP, + ).shift(2.5 * DOWN) + + self.number_line.label_direction = DOWN + + self.number_line_labels = self.number_line.get_number_mobjects() + self.wait() + + origin_point = self.number_line.number_to_point(0) + nl_sources = VMobject() + pond_sources = VMobject() + + for i in range(-MAX_N,MAX_N+1): + anchor = self.number_line.number_to_point(2*i + 1) + ls = self.light_sources_array[i].copy() + ls.move_source_to(anchor) + nl_sources.add(ls) + pond_sources.add(self.light_sources_array[i].copy()) + + self.add(pond_sources) + self.remove(self.light_sources) + + self.outer_lake.rotate(TAU/8) + + # open sea + open_sea = Rectangle( + width = 20, + height = 10, + stroke_width = LAKE_STROKE_WIDTH, + stroke_color = LAKE_STROKE_COLOR, + fill_color = LAKE_COLOR, + fill_opacity = LAKE_OPACITY, + ).flip().next_to(origin_point,UP,buff = 0) + + + + self.play( + ReplacementTransform(pond_sources,nl_sources), + ReplacementTransform(self.outer_lake,open_sea), + FadeOut(self.inner_lake) + ) + self.play(FadeIn(self.number_line)) + + + self.wait() + + v = 5*UP + self.play( + nl_sources.shift,v, + morty.shift,v, + self.number_line.shift,v, + indicator.shift,v, + indicator_reading.shift,v, + open_sea.shift,v, + ) + self.number_line_labels.shift(v) + + origin_point = self.number_line.number_to_point(0) + self.remove(obs_dot) + self.play( + indicator.move_to, origin_point + UP, + indicator_reading.move_to, origin_point + UP, + FadeOut(open_sea), + FadeOut(morty), + FadeIn(self.number_line_labels) + ) + + two_sided_sum = TexMobject("\dots", "+", "{1\over (-11)^2}",\ + "+", "{1\over (-9)^2}", " + ", "{1\over (-7)^2}", " + ", "{1\over (-5)^2}", " + ", \ + "{1\over (-3)^2}", " + ", "{1\over (-1)^2}", " + ", "{1\over 1^2}", " + ", \ + "{1\over 3^2}", " + ", "{1\over 5^2}", " + ", "{1\over 7^2}", " + ", \ + "{1\over 9^2}", " + ", "{1\over 11^2}", " + ", "\dots") + + nb_symbols = len(two_sided_sum.submobjects) + + two_sided_sum.scale(0.8) + + for (i,submob) in zip(range(nb_symbols),two_sided_sum.submobjects): + submob.next_to(self.number_line.number_to_point(i - 13),DOWN, buff = 2) + if (i == 0 or i % 2 == 1 or i == nb_symbols - 1): # non-fractions + submob.shift(0.2 * DOWN) + + self.play(Write(two_sided_sum)) + + covering_rectangle = Rectangle( + width = SPACE_WIDTH, + height = 2 * SPACE_HEIGHT, + stroke_width = 0, + fill_color = BLACK, + fill_opacity = 1, + ) + covering_rectangle.next_to(ORIGIN,LEFT,buff = 0) + for i in range(10): + self.add_foreground_mobject(self.light_sources_array[i]) + + self.add_foreground_mobject(indicator) + self.add_foreground_mobject(indicator.reading) + + half_indicator = indicator.copy() + half_indicator.show_reading = False + half_indicator.set_intensity(indicator.intensity / 2) + half_indicator_reading = TexMobject("{\pi^2 \over 8}").scale(TEX_SCALE) + half_indicator_reading.move_to(half_indicator) + half_indicator.remove(indicator_reading) + half_indicator.add(half_indicator_reading) + + half_indicator_copy = half_indicator.deepcopy() + half_indicator_reading_copy = half_indicator_reading.deepcopy() + + self.play( + FadeIn(covering_rectangle), + FadeIn(half_indicator), + ) + + p = 2*LEFT + self.play( + half_indicator_copy.move_to,p, + half_indicator_reading_copy.move_to,p + ) + + + +class LabeledArc(Arc): + CONFIG = { + "length" : 1 + } + + def __init__(self, angle, **kwargs): + + BUFFER = 1.3 + + Arc.__init__(self,angle,**kwargs) + + label = DecimalNumber(self.length, num_decimal_points = 0) + r = BUFFER * self.radius + theta = self.start_angle + self.angle/2 + label_pos = r * np.array([np.cos(theta), np.sin(theta), 0]) + + label.move_to(label_pos) + self.add(label) + + + + +class ArcHighlightOverlayScene(Scene): + + def construct(self): + + BASELINE_YPOS = -2.5 + OBSERVER_POINT = [0,BASELINE_YPOS,0] + LAKE0_RADIUS = 1.5 + INDICATOR_RADIUS = 0.6 + TICK_SIZE = 0.5 + LIGHTHOUSE_HEIGHT = 0.2 + LAKE_COLOR = BLUE + LAKE_OPACITY = 0.15 + LAKE_STROKE_WIDTH = 5.0 + LAKE_STROKE_COLOR = BLUE + TEX_SCALE = 0.8 + DOT_COLOR = BLUE + + FLASH_TIME = 0.25 + + def flash_arcs(n): + + angle = TAU/2**n + arcs = [] + arcs.append(LabeledArc(angle/2, start_angle = -TAU/4, radius = LAKE0_RADIUS, length = 1)) + + for i in range(1,2**n): + arcs.append(LabeledArc(angle, start_angle = -TAU/4 + (i-0.5)*angle, radius = LAKE0_RADIUS, length = 2)) + + arcs.append(LabeledArc(angle/2, start_angle = -TAU/4 - angle/2, radius = LAKE0_RADIUS, length = 1)) + + self.play( + FadeIn(arcs[0], run_time = FLASH_TIME) + ) + + for i in range(1,2**n + 1): + self.play( + FadeOut(arcs[i-1], run_time = FLASH_TIME), + FadeIn(arcs[i], run_time = FLASH_TIME) + ) + + self.play( + FadeOut(arcs[2**n], run_time = FLASH_TIME), + ) + + + flash_arcs(3) From 25744bdb543e7d5d019c9881527913fbcc5892d5 Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Thu, 22 Feb 2018 11:18:20 +0100 Subject: [PATCH 03/11] rewriting PondScene with ThreeDCamera --- active_projects/basel.py | 112 +++++++++++++++++++-------------------- 1 file changed, 54 insertions(+), 58 deletions(-) diff --git a/active_projects/basel.py b/active_projects/basel.py index 7eb9285a..282ab3ee 100644 --- a/active_projects/basel.py +++ b/active_projects/basel.py @@ -2132,7 +2132,10 @@ class IPTScene2(Scene): self.play(ShowCreation(box),Write(text)) -class PondScene(Scene): +class PondScene(ThreeDScene): + + + def construct(self): @@ -2153,12 +2156,44 @@ class PondScene(Scene): LIGHT_SCALE = 5 LIGHT_CUTOFF = 1 + self.cumulated_zoom_factor = 1 + + + def zoom_out_scene(factor): + phi0 = self.camera.get_phi() # default is 0 degs + theta0 = self.camera.get_theta() # default is -90 degs + distance0 = self.camera.get_distance() + + distance1 = 2 * distance0 + camera_target_point = self.camera.get_spherical_coords(phi0, theta0, distance1) + + self.play( + ApplyMethod(self.camera.rotation_mobject.move_to, camera_target_point), + self.unzoomable_mobs.scale,2,{"about_point" : ORIGIN}, + ) + + self.cumulated_zoom_factor *= factor + + + def shift_scene(v): + self.play( + self.zoomable_mobs.shift,v, + self.unzoomable_mobs.shift,v + ) + + + self.zoomable_mobs = VMobject() + self.unzoomable_mobs = VMobject() + + baseline = VMobject() baseline.set_points_as_corners([[-8,BASELINE_YPOS,0],[8,BASELINE_YPOS,0]]) + baseline.set_fill(opacity = 0) # in case it gets accidentally added to the scene + self.zoomable_mobs.add(baseline) # prob not necessary obs_dot = Dot(OBSERVER_POINT, fill_color = DOT_COLOR) ls0_dot = Dot(OBSERVER_POINT + 2 * LAKE0_RADIUS * UP, fill_color = WHITE) - + self.zoomable_mobs.add(obs_dot, ls0_dot) # lake lake0 = Circle(radius = LAKE0_RADIUS, @@ -2167,6 +2202,7 @@ class PondScene(Scene): fill_opacity = LAKE_OPACITY ) lake0.move_to(OBSERVER_POINT + LAKE0_RADIUS * UP) + self.zoomable_mobs.add(lake0) # Morty and indicator morty = Mortimer().scale(0.3) @@ -2177,11 +2213,13 @@ class PondScene(Scene): color = LIGHT_COLOR ) indicator.next_to(morty,LEFT) + self.unzoomable_mobs.add(morty, indicator) # first lighthouse original_op_func = inverse_quadratic(LIGHT_MAX_INT,LIGHT_SCALE,LIGHT_CUTOFF) ls0 = LightSource(opacity_function = original_op_func) ls0.move_source_to(OBSERVER_POINT + LAKE0_RADIUS * 2 * UP) + self.zoomable_mobs.add(ls0, ls0.lighthouse, ls0.ambient_light) self.add(lake0,morty,obs_dot,ls0_dot, ls0.lighthouse) @@ -2249,6 +2287,7 @@ class PondScene(Scene): indicator_reading = TexMobject("{1\over d^2}").scale(TEX_SCALE) indicator_reading.move_to(indicator) + self.unzoomable_mobs.add(indicator_reading) self.play( FadeIn(indicator_reading) @@ -2306,7 +2345,7 @@ class PondScene(Scene): return position - def split_light_source(i, step, show_steps = True, run_time = 1, scale_down = True): + def split_light_source(i, step, show_steps = True, run_time = 1): ls_new_loc1 = position_for_index(i,step + 1) ls_new_loc2 = position_for_index(i + 2**step,step + 1) @@ -2335,22 +2374,6 @@ class PondScene(Scene): ls1 = self.light_sources_array[i] - if scale_down: - # print "scaling down" - # #new_of = lambda x: original_op_func(x/0.5**step) - # #new_of = inverse_quadratic(LIGHT_MAX_INT, LIGHT_SCALE * 10**(step+1), LIGHT_CUTOFF) - # print "==============" - # for t in np.arange(0.0,5.0,1.0): - # print ls1.ambient_light.opacity_function(t) - - # print "==============" - - new_of = lambda x: np.sin(10*x) - ls1.ambient_light.change_opacity_function(lambda x: np.sin(100*x)) - # for t in np.arange(0.0,5.0,1.0): - # print ls1.ambient_light.opacity_function(t) - - ls1.spotlight.change_opacity_function(new_of) ls2 = ls1.copy() self.add(ls2) @@ -2376,7 +2399,7 @@ class PondScene(Scene): - def construction_step(n, scale_down = True, show_steps = True, run_time = 1, + def construction_step(n, show_steps = True, run_time = 1, simultaneous_splitting = False): # we assume that the scene contains: @@ -2441,8 +2464,7 @@ class PondScene(Scene): split_light_source(i, step = n, show_steps = show_steps, - run_time = run_time, - scale_down = scale_down + run_time = run_time ) @@ -2484,37 +2506,8 @@ class PondScene(Scene): if show_steps == True: self.play(FadeOut(ls0_dot)) - # scale down - if scale_down: - - indicator_wiggle() - - if show_steps == True: - self.play( - ScaleLightSources(self.light_sources,0.5,about_point = OBSERVER_POINT), - self.inner_lake.scale_about_point,0.5,OBSERVER_POINT, - self.outer_lake.scale_about_point,0.5,OBSERVER_POINT, - self.legs.scale_about_point,0.5,OBSERVER_POINT, - self.hypotenuses.scale_about_point,0.5,OBSERVER_POINT, - self.altitudes.scale_about_point,0.5,OBSERVER_POINT, - ) - else: - self.play( - ScaleLightSources(self.light_sources,0.5,about_point = OBSERVER_POINT), - self.inner_lake.scale_about_point,0.5,OBSERVER_POINT, - self.outer_lake.scale_about_point,0.5,OBSERVER_POINT, - ) - - # update the radii bc they haven't done so themselves - # bc reasons... - for ls in self.light_sources_array: - r = ls.radius - ls.set_radius(r*0.5) - - else: - # update the lake center and the radius - self.lake_center = ls0_loc = self.outer_lake.get_center() + self.lake_radius * UP - self.lake_radius *= 2 + self.lake_center = ls0_loc = self.outer_lake.get_center() + self.lake_radius * UP + self.lake_radius *= 2 @@ -2537,6 +2530,8 @@ class PondScene(Scene): self.lake_radius = 2 * LAKE0_RADIUS # don't ask... + self.zoomable_mobs.add(self.inner_lake, self.outer_lake, self.altitudes, self.light_sources) + self.add(self.inner_lake, self.outer_lake, self.legs, @@ -2551,10 +2546,12 @@ class PondScene(Scene): self.new_legs_2 = [] self.new_hypotenuses = [] - ls_radius = 25.0 + for i in range(3): - construction_step(i, scale_down = True) + construction_step(i) + indicator_wiggle() + zoom_out_scene(2) @@ -2565,11 +2562,10 @@ class PondScene(Scene): ) for i in range(3,5): - construction_step(i, scale_down = False, show_steps = False, run_time = 1.0/2**i, - simultaneous_splitting = True) - + construction_step(i, show_steps = False, run_time = 1.0/2**i) + return # Now create a straight number line and transform into it MAX_N = 17 From fd91761f892a2b68a78fedc9287b70b264f6b85e Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Thu, 22 Feb 2018 18:20:25 +0100 Subject: [PATCH 04/11] Finalized PondScene (save overlays), working on final scenes --- active_projects/basel.py | 226 ++++++++++++++++++++++++++++----------- 1 file changed, 163 insertions(+), 63 deletions(-) diff --git a/active_projects/basel.py b/active_projects/basel.py index 282ab3ee..8bd8a8d8 100644 --- a/active_projects/basel.py +++ b/active_projects/basel.py @@ -2132,6 +2132,27 @@ class IPTScene2(Scene): self.play(ShowCreation(box),Write(text)) + +BASELINE_YPOS = -2.5 +OBSERVER_POINT = [0,BASELINE_YPOS,0] +LAKE0_RADIUS = 1.5 +INDICATOR_RADIUS = 0.6 +TICK_SIZE = 0.5 +LIGHTHOUSE_HEIGHT = 0.2 +LAKE_COLOR = BLUE +LAKE_OPACITY = 0.15 +LAKE_STROKE_WIDTH = 5.0 +LAKE_STROKE_COLOR = BLUE +TEX_SCALE = 0.8 +DOT_COLOR = BLUE + +LIGHT_MAX_INT = 1 +LIGHT_SCALE = 5 +LIGHT_CUTOFF = 1 + + + + class PondScene(ThreeDScene): @@ -2139,27 +2160,14 @@ class PondScene(ThreeDScene): def construct(self): - BASELINE_YPOS = -2.5 - OBSERVER_POINT = [0,BASELINE_YPOS,0] - LAKE0_RADIUS = 1.5 - INDICATOR_RADIUS = 0.6 - TICK_SIZE = 0.5 - LIGHTHOUSE_HEIGHT = 0.2 - LAKE_COLOR = BLUE - LAKE_OPACITY = 0.15 - LAKE_STROKE_WIDTH = 5.0 - LAKE_STROKE_COLOR = BLUE - TEX_SCALE = 0.8 - DOT_COLOR = BLUE - - LIGHT_MAX_INT = 1 - LIGHT_SCALE = 5 - LIGHT_CUTOFF = 1 self.cumulated_zoom_factor = 1 + self.force_skipping() + def zoom_out_scene(factor): + phi0 = self.camera.get_phi() # default is 0 degs theta0 = self.camera.get_theta() # default is -90 degs distance0 = self.camera.get_distance() @@ -2169,6 +2177,7 @@ class PondScene(ThreeDScene): self.play( ApplyMethod(self.camera.rotation_mobject.move_to, camera_target_point), + self.zoomable_mobs.shift, self.obs_dot.get_center(), self.unzoomable_mobs.scale,2,{"about_point" : ORIGIN}, ) @@ -2188,12 +2197,12 @@ class PondScene(ThreeDScene): baseline = VMobject() baseline.set_points_as_corners([[-8,BASELINE_YPOS,0],[8,BASELINE_YPOS,0]]) - baseline.set_fill(opacity = 0) # in case it gets accidentally added to the scene + baseline.set_stroke(width = 0) # in case it gets accidentally added to the scene self.zoomable_mobs.add(baseline) # prob not necessary - obs_dot = Dot(OBSERVER_POINT, fill_color = DOT_COLOR) + self.obs_dot = Dot(OBSERVER_POINT, fill_color = DOT_COLOR) ls0_dot = Dot(OBSERVER_POINT + 2 * LAKE0_RADIUS * UP, fill_color = WHITE) - self.zoomable_mobs.add(obs_dot, ls0_dot) + self.unzoomable_mobs.add(self.obs_dot, ls0_dot) # lake lake0 = Circle(radius = LAKE0_RADIUS, @@ -2221,7 +2230,7 @@ class PondScene(ThreeDScene): ls0.move_source_to(OBSERVER_POINT + LAKE0_RADIUS * 2 * UP) self.zoomable_mobs.add(ls0, ls0.lighthouse, ls0.ambient_light) - self.add(lake0,morty,obs_dot,ls0_dot, ls0.lighthouse) + self.add(lake0,morty,self.obs_dot,ls0_dot, ls0.lighthouse) self.wait() @@ -2281,7 +2290,7 @@ class PondScene(ThreeDScene): self.play( ShowCreation(diameter), Write(diameter_text), - #FadeOut(obs_dot), + #FadeOut(self.obs_dot), FadeOut(ls0_dot) ) @@ -2340,7 +2349,7 @@ class PondScene(ThreeDScene): position = self.lake_center + self.lake_radius * radial_vector if scaled_down: - return position.scale_about_point(OBSERVER_POINT,0.5) + return position.scale_about_point(self.obs_dot.get_center(),0.5) else: return position @@ -2361,8 +2370,8 @@ class PondScene(ThreeDScene): ShowCreation(hyp, run_time = run_time) ) - leg1 = Line(OBSERVER_POINT,ls_new_loc1) - leg2 = Line(OBSERVER_POINT,ls_new_loc2) + leg1 = Line(self.obs_dot.get_center(),ls_new_loc1) + leg2 = Line(self.obs_dot.get_center(),ls_new_loc2) self.new_legs_1.append(leg1) self.new_legs_2.append(leg2) @@ -2381,9 +2390,9 @@ class PondScene(ThreeDScene): # check if the light sources are on screen ls_old_loc = np.array(ls1.get_source_point()) - onscreen_old = np.all(np.abs(ls_old_loc) < 10) - onscreen_1 = np.all(np.abs(ls_new_loc1) < 10) - onscreen_2 = np.all(np.abs(ls_new_loc2) < 10) + onscreen_old = np.any(np.abs(ls_old_loc) < 10) + onscreen_1 = np.any(np.abs(ls_new_loc1) < 10) + onscreen_2 = np.any(np.abs(ls_new_loc2) < 10) show_animation = (onscreen_old or onscreen_1 or onscreen_2) if show_animation: @@ -2418,18 +2427,22 @@ class PondScene(ThreeDScene): # first, fade out all of the hypotenuses and altitudes + if show_steps == True: + self.zoomable_mobs.remove(self.hypotenuses, self.altitudes, self.inner_lake) self.play( FadeOut(self.hypotenuses), FadeOut(self.altitudes), FadeOut(self.inner_lake) ) else: + self.zoomable_mobs.remove(self.inner_lake) self.play( FadeOut(self.inner_lake) ) # create a new, outer lake + self.lake_center = self.obs_dot.get_center() + self.lake_radius * UP new_outer_lake = Circle(radius = self.lake_radius, stroke_width = LAKE_STROKE_WIDTH, @@ -2454,11 +2467,12 @@ class PondScene(ThreeDScene): self.inner_lake = self.outer_lake self.outer_lake = new_outer_lake self.altitudes = self.legs + #self.lake_center = self.outer_lake.get_center() self.additional_light_sources = [] self.new_legs_1 = [] self.new_legs_2 = [] - self.new_hypotenuses = [] + self.new_hypotenuses = [] for i in range(2**n): split_light_source(i, @@ -2475,16 +2489,23 @@ class PondScene(ThreeDScene): self.legs = VMobject() for leg in self.new_legs_1: self.legs.add(leg) + self.zoomable_mobs.add(leg) for leg in self.new_legs_2: self.legs.add(leg) + self.zoomable_mobs.add(leg) + + for hyp in self.hypotenuses.submobjects: + self.zoomable_mobs.remove(hyp) self.hypotenuses = VMobject() for hyp in self.new_hypotenuses: self.hypotenuses.add(hyp) + self.zoomable_mobs.add(hyp) for ls in self.additional_light_sources: self.light_sources.add(ls) self.light_sources_array.append(ls) + self.zoomable_mobs.add(ls) # update scene self.add( @@ -2492,6 +2513,7 @@ class PondScene(ThreeDScene): self.inner_lake, self.outer_lake, ) + self.zoomable_mobs.add(self.light_sources, self.inner_lake, self.outer_lake) if show_steps == True: self.add( @@ -2499,6 +2521,7 @@ class PondScene(ThreeDScene): self.hypotenuses, self.altitudes, ) + self.zoomable_mobs.add(self.legs, self.hypotenuses, self.altitudes) self.wait() @@ -2506,7 +2529,7 @@ class PondScene(ThreeDScene): if show_steps == True: self.play(FadeOut(ls0_dot)) - self.lake_center = ls0_loc = self.outer_lake.get_center() + self.lake_radius * UP + #self.lake_center = ls0_loc = self.obs_dot.get_center() + self.lake_radius * UP self.lake_radius *= 2 @@ -2547,11 +2570,20 @@ class PondScene(ThreeDScene): self.new_hypotenuses = [] + construction_step(0) + indicator_wiggle() + self.play(FadeOut(ls0_dot)) + zoom_out_scene(2) + + construction_step(1) + indicator_wiggle() + self.play(FadeOut(ls0_dot)) + zoom_out_scene(2) + + construction_step(2) + indicator_wiggle() + self.play(FadeOut(ls0_dot)) - for i in range(3): - construction_step(i) - indicator_wiggle() - zoom_out_scene(2) @@ -2559,17 +2591,22 @@ class PondScene(ThreeDScene): FadeOut(self.altitudes), FadeOut(self.hypotenuses), FadeOut(self.legs) - ) + ) - for i in range(3,5): - construction_step(i, show_steps = False, run_time = 1.0/2**i) + max_it = 6 + scale = 2**(max_it - 4) + TEX_SCALE *= scale + for i in range(3,max_it + 1): + construction_step(i, show_steps = False, run_time = 4.0/2**i) - return + self.revert_to_original_skipping_status() # Now create a straight number line and transform into it MAX_N = 17 + origin_point = self.obs_dot.get_center() + self.number_line = NumberLine( x_min = -MAX_N, x_max = MAX_N + 1, @@ -2579,11 +2616,11 @@ class PondScene(ThreeDScene): stroke_color = LAKE_STROKE_COLOR, #numbers_with_elongated_ticks = range(-MAX_N,MAX_N + 1), numbers_to_show = range(-MAX_N,MAX_N + 1,2), - unit_size = LAKE0_RADIUS * TAU/4 / 4, + unit_size = LAKE0_RADIUS * TAU/4 / 2 * scale, tick_frequency = 1, line_to_number_buff = LARGE_BUFF, label_direction = UP, - ).shift(2.5 * DOWN) + ).shift(scale * 2.5 * DOWN) self.number_line.label_direction = DOWN @@ -2608,8 +2645,8 @@ class PondScene(ThreeDScene): # open sea open_sea = Rectangle( - width = 20, - height = 10, + width = 20 * scale, + height = 10 * scale, stroke_width = LAKE_STROKE_WIDTH, stroke_color = LAKE_STROKE_COLOR, fill_color = LAKE_COLOR, @@ -2628,7 +2665,7 @@ class PondScene(ThreeDScene): self.wait() - v = 5*UP + v = 5 * scale * UP self.play( nl_sources.shift,v, morty.shift,v, @@ -2640,10 +2677,10 @@ class PondScene(ThreeDScene): self.number_line_labels.shift(v) origin_point = self.number_line.number_to_point(0) - self.remove(obs_dot) + #self.remove(self.obs_dot) self.play( - indicator.move_to, origin_point + UP, - indicator_reading.move_to, origin_point + UP, + indicator.move_to, origin_point + scale * UP, + indicator_reading.move_to, origin_point + scale * UP, FadeOut(open_sea), FadeOut(morty), FadeIn(self.number_line_labels) @@ -2657,51 +2694,114 @@ class PondScene(ThreeDScene): nb_symbols = len(two_sided_sum.submobjects) - two_sided_sum.scale(0.8) + two_sided_sum.scale(TEX_SCALE) for (i,submob) in zip(range(nb_symbols),two_sided_sum.submobjects): - submob.next_to(self.number_line.number_to_point(i - 13),DOWN, buff = 2) + submob.next_to(self.number_line.number_to_point(i - 13),DOWN, buff = 2*scale) if (i == 0 or i % 2 == 1 or i == nb_symbols - 1): # non-fractions - submob.shift(0.2 * DOWN) + submob.shift(0.2 * scale * DOWN) self.play(Write(two_sided_sum)) covering_rectangle = Rectangle( - width = SPACE_WIDTH, - height = 2 * SPACE_HEIGHT, + width = SPACE_WIDTH * scale, + height = 2 * SPACE_HEIGHT * scale, stroke_width = 0, fill_color = BLACK, fill_opacity = 1, ) covering_rectangle.next_to(ORIGIN,LEFT,buff = 0) for i in range(10): - self.add_foreground_mobject(self.light_sources_array[i]) + self.add_foreground_mobject(nl_sources.submobjects[i]) self.add_foreground_mobject(indicator) - self.add_foreground_mobject(indicator.reading) + self.add_foreground_mobject(indicator_reading) - half_indicator = indicator.copy() - half_indicator.show_reading = False - half_indicator.set_intensity(indicator.intensity / 2) half_indicator_reading = TexMobject("{\pi^2 \over 8}").scale(TEX_SCALE) half_indicator_reading.move_to(half_indicator) - half_indicator.remove(indicator_reading) - half_indicator.add(half_indicator_reading) - half_indicator_copy = half_indicator.deepcopy() - half_indicator_reading_copy = half_indicator_reading.deepcopy() + central_plus_sign = two_sided_sum[13] self.play( FadeIn(covering_rectangle), - FadeIn(half_indicator), + ReplacementTransform(indicator_reading, half_indicator_reading), + FadeOut(central_plus_sign) ) - p = 2*LEFT + equals_sign = TexMobject("=").scale(TEX_SCALE) + equals_sign.move_to(central_plus_sign) + p = 2 * scale * LEFT + self.play( - half_indicator_copy.move_to,p, - half_indicator_reading_copy.move_to,p + indicator.move_to,p, + half_indicator_reading.move_to,p, + FadeIn(equals_sign) ) + # show Randy admiring the result + randy = Randolph().scale(scale).move_to(2*scale*DOWN+5*scale*LEFT) + self.play(FadeIn(randy)) + self.play(randy.change,"happy") + + + +class WaitScene(TeacherStudentsScene): + + def construct(self): + + self.teacher_says(TexMobject("{1\over 1^2}+{1\over 3^2}+{1\over 5^2}+{1\over 7^2}+\dots = {\pi^2 \over 8}!")) + + student_q = TextMobject("What about") + full_sum = TexMobject("{1\over 1^2}+{1\over 2^2}+{1\over 3^2}+{1\over 4^2}+\dots?") + full_sum.next_to(student_q,RIGHT) + student_q.add(full_sum) + + self.student_says(student_q) + + +class FinalSumManipulationScene(PiCreatureScene): + + def construct(self): + + odd_range = range(1,11,2) + even_range = range(2,12,2) + full_range = range(1,12,1) + + self.number_line = NumberLine( + x_min = 0, + x_max = 10, + color = LAKE_STROKE_COLOR, + number_at_center = 0, + stroke_width = LAKE_STROKE_WIDTH, + stroke_color = LAKE_STROKE_COLOR, + numbers_to_show = odd_range, + unit_size = 1, + tick_frequency = 1, + line_to_number_buff = LARGE_BUFF, + label_direction = UP, + ) + + odd_lights = VMobject() + + for i in odd_range: + + pos = self.number_line.number_to_point(i) + ls = LightSource() + ls.move_source_to(pos) + odd_lights.add(ls) + + self.play(ShowCreation(self.number_line)) + self.play(FadeIn(odd_lights)) + + + + + + + + + + class LabeledArc(Arc): From d416890fbcc643111c4577c21e4865aa55df03db Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Thu, 22 Feb 2018 23:02:54 +0100 Subject: [PATCH 05/11] added synonym for number line labels --- topics/number_line.py | 27 ++++----------------------- 1 file changed, 4 insertions(+), 23 deletions(-) diff --git a/topics/number_line.py b/topics/number_line.py index 9d02286c..3848feba 100644 --- a/topics/number_line.py +++ b/topics/number_line.py @@ -120,6 +120,9 @@ class NumberLine(VMobject): result.add(mob) return result + def get_labels(self): + return self.get_number_mobjects() + def add_numbers(self, *numbers, **kwargs): self.numbers = self.get_number_mobjects( *numbers, **kwargs @@ -219,29 +222,7 @@ class Axes(VGroup): return graph def input_to_graph_point(self, x, graph): - if hasattr(graph, "underlying_function"): - return self.coords_to_point(x, graph.underlying_function(x)) - else: - #binary search - lh, rh = 0, 1 - while abs(lh - rh) > 0.001: - mh = np.mean([lh, rh]) - hands = [lh, mh, rh] - points = map(graph.point_from_proportion, hands) - lx, mx, rx = map(self.x_axis.point_to_number, points) - if lx <= x and rx >= x: - if mx > x: - rh = mh - else: - lh = mh - elif lx <= x and rx <= x: - return points[2] - elif lx >= x and rx >= x: - return points[0] - elif lx > x and rx < x: - lh, rh = rh, lh - return points[1] - + return self.coords_to_point(x, graph.underlying_function(x)) class ThreeDAxes(Axes): CONFIG = { From 4f8bc1fee928d409f7b1ffbc39c0b03183577c18 Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Thu, 22 Feb 2018 23:03:05 +0100 Subject: [PATCH 06/11] working on final scene --- active_projects/basel.py | 185 ++++++++++++++++++++++++++++++++------- 1 file changed, 153 insertions(+), 32 deletions(-) diff --git a/active_projects/basel.py b/active_projects/basel.py index 8bd8a8d8..757b4254 100644 --- a/active_projects/basel.py +++ b/active_projects/basel.py @@ -2133,22 +2133,7 @@ class IPTScene2(Scene): -BASELINE_YPOS = -2.5 -OBSERVER_POINT = [0,BASELINE_YPOS,0] -LAKE0_RADIUS = 1.5 -INDICATOR_RADIUS = 0.6 -TICK_SIZE = 0.5 -LIGHTHOUSE_HEIGHT = 0.2 -LAKE_COLOR = BLUE -LAKE_OPACITY = 0.15 -LAKE_STROKE_WIDTH = 5.0 -LAKE_STROKE_COLOR = BLUE -TEX_SCALE = 0.8 -DOT_COLOR = BLUE -LIGHT_MAX_INT = 1 -LIGHT_SCALE = 5 -LIGHT_CUTOFF = 1 @@ -2160,10 +2145,26 @@ class PondScene(ThreeDScene): def construct(self): + BASELINE_YPOS = -2.5 + OBSERVER_POINT = [0,BASELINE_YPOS,0] + LAKE0_RADIUS = 1.5 + INDICATOR_RADIUS = 0.6 + TICK_SIZE = 0.5 + LIGHTHOUSE_HEIGHT = 0.2 + LAKE_COLOR = BLUE + LAKE_OPACITY = 0.15 + LAKE_STROKE_WIDTH = 5.0 + LAKE_STROKE_COLOR = BLUE + TEX_SCALE = 0.8 + DOT_COLOR = BLUE + + LIGHT_MAX_INT = 1 + LIGHT_SCALE = 5 + LIGHT_CUTOFF = 1 self.cumulated_zoom_factor = 1 - self.force_skipping() + #self.force_skipping() def zoom_out_scene(factor): @@ -2600,7 +2601,7 @@ class PondScene(ThreeDScene): for i in range(3,max_it + 1): construction_step(i, show_steps = False, run_time = 4.0/2**i) - self.revert_to_original_skipping_status() + #self.revert_to_original_skipping_status() # Now create a straight number line and transform into it MAX_N = 17 @@ -2718,7 +2719,7 @@ class PondScene(ThreeDScene): self.add_foreground_mobject(indicator_reading) half_indicator_reading = TexMobject("{\pi^2 \over 8}").scale(TEX_SCALE) - half_indicator_reading.move_to(half_indicator) + half_indicator_reading.move_to(indicator) central_plus_sign = two_sided_sum[13] @@ -2756,42 +2757,159 @@ class WaitScene(TeacherStudentsScene): full_sum.next_to(student_q,RIGHT) student_q.add(full_sum) - self.student_says(student_q) + + self.student_says(student_q, target_mode = "angry") class FinalSumManipulationScene(PiCreatureScene): def construct(self): - odd_range = range(1,11,2) - even_range = range(2,12,2) - full_range = range(1,12,1) + LAKE_COLOR = BLUE + LAKE_OPACITY = 0.15 + LAKE_STROKE_WIDTH = 5.0 + LAKE_STROKE_COLOR = BLUE + TEX_SCALE = 0.8 - self.number_line = NumberLine( + unit_length = 1.5 + vertical_spacing = 1.8 * DOWN + switch_on_time = 0.2 + + randy = self.get_primary_pi_creature() + randy.scale(0.7).to_edge(DOWN + RIGHT) + + ls_template = LightSource( + radius = 2, + max_opacity_ambient = 0.5, + opacity_function = inverse_quadratic(1,0.5,1) + ) + + + odd_range = np.arange(1,13,2) + even_range = np.arange(2,28,2) + full_range = np.arange(1,14,1) + + self.number_line1 = NumberLine( x_min = 0, - x_max = 10, + x_max = 11, color = LAKE_STROKE_COLOR, number_at_center = 0, stroke_width = LAKE_STROKE_WIDTH, stroke_color = LAKE_STROKE_COLOR, numbers_to_show = odd_range, - unit_size = 1, + unit_size = unit_length, tick_frequency = 1, - line_to_number_buff = LARGE_BUFF, - label_direction = UP, + line_to_number_buff = MED_LARGE_BUFF, + include_tip = True ) + self.number_line1.next_to(3 * UP + 3 * LEFT, RIGHT, buff = 0) + odd_lights = VMobject() for i in odd_range: - - pos = self.number_line.number_to_point(i) - ls = LightSource() + pos = self.number_line1.number_to_point(i) + ls = ls_template.copy() ls.move_source_to(pos) odd_lights.add(ls) - self.play(ShowCreation(self.number_line)) - self.play(FadeIn(odd_lights)) + labels1 = self.number_line1.get_labels() + self.play( + ShowCreation(self.number_line1), + ShowCreation(labels1) + ) + + for ls in odd_lights.submobjects: + self.play(SwitchOn(ls.ambient_light), run_time = switch_on_time) + + result1 = TexMobject("{\pi^2\over 8} =") + result1.next_to(self.number_line1, LEFT, buff = 2) + self.play(Write(result1)) + + self.number_line2 = self.number_line1.copy() + self.number_line2.numbers_to_show = full_range + self.number_line2.shift(2 * vertical_spacing) + labels2 = self.number_line2.get_labels() + + full_lights = VMobject() + + for i in full_range: + pos = self.number_line2.number_to_point(i) + ls = ls_template.copy() + ls.move_source_to(pos) + full_lights.add(ls) + + self.play( + ShowCreation(self.number_line2), + ShowCreation(labels2) + ) + + for ls in full_lights.submobjects: + self.play(SwitchOn(ls.ambient_light, run_time = 0.1 * switch_on_time)) + + + self.number_line3 = self.number_line1.copy() + self.number_line3.numbers_to_show = even_range + self.number_line3.shift(vertical_spacing) + labels3 = self.number_line3.get_labels() + + missing_text = TextMobject("missing:") + missing_text.next_to(self.number_line3, LEFT, buff = 2) + + even_lights = VMobject() + + for i in even_range: + pos = self.number_line3.number_to_point(i) + ls = ls_template.copy() + ls.move_source_to(pos) + even_lights.add(ls) + + self.play(Write(missing_text)) + + self.play( + ShowCreation(self.number_line3), + ShowCreation(labels3), + ) + + for ls in even_lights.submobjects: + self.play(SwitchOn(ls.ambient_light), run_time = switch_on_time) + + + # now morph the even lights into the full lights + even_lights_line = VMobject() + even_lights_line.add(even_lights, self.number_line2) + even_lights_line_copy = even_lights_line.copy() + + full_lights_line = VMobject() + number_line2p = self.number_line2 + number_line2p.tick_frequency = 0.5 + full_lights_line.add(full_lights.copy(), number_line2p) + + self.play( + Transform(even_lights_line,full_lights_line) + ) + + self.play( + Transform(even_lights_line,even_lights_line_copy) + ) + + + + + + + + + + + + + + + + + + @@ -2826,6 +2944,9 @@ class LabeledArc(Arc): + + + class ArcHighlightOverlayScene(Scene): def construct(self): From e6dca3b44d1a79ba4c6be7d174cc1688caaac680 Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Fri, 23 Feb 2018 14:44:29 +0100 Subject: [PATCH 07/11] finished sum manipulation scene --- active_projects/basel.py | 198 +++++++++++++++++++++++++++++++-------- 1 file changed, 158 insertions(+), 40 deletions(-) diff --git a/active_projects/basel.py b/active_projects/basel.py index 757b4254..1f3b49f6 100644 --- a/active_projects/basel.py +++ b/active_projects/basel.py @@ -2771,12 +2771,17 @@ class FinalSumManipulationScene(PiCreatureScene): LAKE_STROKE_COLOR = BLUE TEX_SCALE = 0.8 + LIGHT_COLOR2 = RED + LIGHT_COLOR3 = BLUE + unit_length = 1.5 - vertical_spacing = 1.8 * DOWN + vertical_spacing = 2.5 * DOWN switch_on_time = 0.2 + sum_vertical_spacing = 1.5 + randy = self.get_primary_pi_creature() - randy.scale(0.7).to_edge(DOWN + RIGHT) + randy.scale(0.7).flip().to_edge(DOWN + LEFT) ls_template = LightSource( radius = 2, @@ -2785,9 +2790,9 @@ class FinalSumManipulationScene(PiCreatureScene): ) - odd_range = np.arange(1,13,2) - even_range = np.arange(2,28,2) - full_range = np.arange(1,14,1) + odd_range = np.arange(1,9,2) + even_range = np.arange(2,16,2) + full_range = np.arange(1,8,1) self.number_line1 = NumberLine( x_min = 0, @@ -2803,107 +2808,220 @@ class FinalSumManipulationScene(PiCreatureScene): include_tip = True ) - self.number_line1.next_to(3 * UP + 3 * LEFT, RIGHT, buff = 0) + self.number_line1.next_to(2.5 * UP + 3 * LEFT, RIGHT, buff = 0) odd_lights = VMobject() - for i in odd_range: pos = self.number_line1.number_to_point(i) ls = ls_template.copy() ls.move_source_to(pos) odd_lights.add(ls) - labels1 = self.number_line1.get_labels() self.play( ShowCreation(self.number_line1), - ShowCreation(labels1) ) - for ls in odd_lights.submobjects: - self.play(SwitchOn(ls.ambient_light), run_time = switch_on_time) + odd_terms = VMobject() + for i in odd_range: + if i == 1: + term = TexMobject("\phantom{+\,}{1\over " + str(i) + "^2}", fill_color = LIGHT_COLOR) + else: + term = TexMobject("+\, {1\over " + str(i) + "^2}", fill_color = LIGHT_COLOR) - result1 = TexMobject("{\pi^2\over 8} =") - result1.next_to(self.number_line1, LEFT, buff = 2) + term.next_to(self.number_line1.number_to_point(i), DOWN, buff = 1.5) + odd_terms.add(term) + + + for (ls, term) in zip(odd_lights.submobjects, odd_terms.submobjects): + self.play( + FadeIn(ls.lighthouse, run_time = switch_on_time), + SwitchOn(ls.ambient_light, run_time = switch_on_time), + Write(term, run_time = switch_on_time) + ) + + result1 = TexMobject("{\pi^2\over 8} =", fill_color = LIGHT_COLOR) + result1.next_to(self.number_line1, LEFT, buff = 0.5) self.play(Write(result1)) + + + self.number_line2 = self.number_line1.copy() self.number_line2.numbers_to_show = full_range self.number_line2.shift(2 * vertical_spacing) - labels2 = self.number_line2.get_labels() full_lights = VMobject() for i in full_range: pos = self.number_line2.number_to_point(i) ls = ls_template.copy() + ls.color = LIGHT_COLOR3 ls.move_source_to(pos) full_lights.add(ls) self.play( ShowCreation(self.number_line2), - ShowCreation(labels2) ) + + for ls in full_lights.submobjects: - self.play(SwitchOn(ls.ambient_light, run_time = 0.1 * switch_on_time)) + self.play( + FadeIn(ls.lighthouse, run_time = 0.1),#5 * switch_on_time), + SwitchOn(ls.ambient_light, run_time = 0.1)#5 * switch_on_time), + ) - self.number_line3 = self.number_line1.copy() - self.number_line3.numbers_to_show = even_range - self.number_line3.shift(vertical_spacing) - labels3 = self.number_line3.get_labels() - missing_text = TextMobject("missing:") - missing_text.next_to(self.number_line3, LEFT, buff = 2) + even_terms = VMobject() + for i in even_range: + term = TexMobject("+\, {1\over " + str(i) + "^2}", fill_color = LIGHT_COLOR2) + term.next_to(self.number_line1.number_to_point(i), DOWN, buff = sum_vertical_spacing) + even_terms.add(term) + even_lights = VMobject() for i in even_range: - pos = self.number_line3.number_to_point(i) + pos = self.number_line1.number_to_point(i) ls = ls_template.copy() + ls.color = LIGHT_COLOR2 ls.move_source_to(pos) even_lights.add(ls) - self.play(Write(missing_text)) + for (ls, term) in zip(even_lights.submobjects, even_terms.submobjects): + self.play( + SwitchOn(ls.ambient_light, run_time = switch_on_time), + Write(term) + ) - self.play( - ShowCreation(self.number_line3), - ShowCreation(labels3), - ) - - for ls in even_lights.submobjects: - self.play(SwitchOn(ls.ambient_light), run_time = switch_on_time) # now morph the even lights into the full lights - even_lights_line = VMobject() - even_lights_line.add(even_lights, self.number_line2) - even_lights_line_copy = even_lights_line.copy() + full_lights_copy = full_lights.copy() + even_lights_copy = even_lights.copy() - full_lights_line = VMobject() - number_line2p = self.number_line2 - number_line2p.tick_frequency = 0.5 - full_lights_line.add(full_lights.copy(), number_line2p) self.play( - Transform(even_lights_line,full_lights_line) + Transform(even_lights,full_lights) + ) + + # draw arrows + P1 = self.number_line2.number_to_point(1) + P2 = even_terms.submobjects[0].get_center() + Q1 = interpolate(P1, P2, 0.2) + Q2 = interpolate(P1, P2, 0.8) + quarter_arrow = Arrow(Q1, Q2, + color = LIGHT_COLOR2) + quarter_label = TexMobject("\\times {1\over 4}", fill_color = LIGHT_COLOR2) + quarter_label.scale(0.7) + quarter_label.next_to(quarter_arrow.get_center(), RIGHT) + + self.play( + ShowCreation(quarter_arrow), + Write(quarter_label), + Transform(even_lights,even_lights_copy) + ) + + P3 = odd_terms.submobjects[0].get_center() + R1 = interpolate(P1, P3, 0.2) + R2 = interpolate(P1, P3, 0.8) + three_quarters_arrow = Arrow(R1, R2, + color = LIGHT_COLOR) + three_quarters_label = TexMobject("\\times {3\over 4}", fill_color = LIGHT_COLOR) + three_quarters_label.scale(0.7) + three_quarters_label.next_to(three_quarters_arrow.get_center(), LEFT) + + self.play( + ShowCreation(three_quarters_arrow), + Write(three_quarters_label) + ) + + four_thirds_arrow = Arrow(R2, R1, color = LIGHT_COLOR) + four_thirds_label = TexMobject("\\times {4\over 3}", fill_color = LIGHT_COLOR) + four_thirds_label.scale(0.7) + four_thirds_label.next_to(four_thirds_arrow.get_center(), LEFT) + + self.play( + ReplacementTransform(three_quarters_arrow, four_thirds_arrow), + ReplacementTransform(three_quarters_label, four_thirds_label) ) self.play( - Transform(even_lights_line,even_lights_line_copy) + FadeOut(quarter_label), + FadeOut(quarter_arrow), + FadeOut(even_lights), + FadeOut(even_terms) + ) + full_terms = VMobject() + for i in full_range: + if i == 1: + term = TexMobject("\phantom{+\,}{1\over " + str(i) + "^2}", fill_color = LIGHT_COLOR3) + else: + term = TexMobject("+\, {1\over " + str(i) + "^2}", fill_color = LIGHT_COLOR3) + term.move_to(self.number_line2.number_to_point(i)) + full_terms.add(term) + self.play( + FadeOut(self.number_line1), + FadeOut(odd_lights), + FadeOut(self.number_line2), + FadeOut(full_lights), + FadeIn(full_terms) + ) + v = (sum_vertical_spacing + 0.5) * UP + self.play( + odd_terms.shift, v, + four_thirds_arrow.shift, v, + four_thirds_label.shift, v, + odd_terms.shift, v, + full_terms.shift, v + ) + arrow_copy = four_thirds_arrow.copy() + label_copy = four_thirds_label.copy() + arrow_copy.shift(2.5 * LEFT) + label_copy.shift(2.5 * LEFT) + self.play( + FadeIn(arrow_copy), + FadeIn(label_copy) + ) + final_result = TexMobject("{\pi^2 \over 6}=", fill_color = LIGHT_COLOR3) + final_result.next_to(arrow_copy, DOWN) + self.play( + Write(final_result), + randy.change_mode,"hooray" + ) + equation = VMobject() + equation.add(final_result) + equation.add(full_terms) + buffer = 2 + result_box = Rectangle(width = 15, + height = buffer*equation.get_height(), color = LIGHT_COLOR3) + result_box.move_to(equation) + equation.add(result_box) + self.play( + FadeOut(result1), + FadeOut(odd_terms), + FadeOut(arrow_copy), + FadeOut(label_copy), + FadeOut(four_thirds_arrow), + FadeOut(four_thirds_label), + ShowCreation(result_box) + ) + + self.play(equation.shift, -equation.get_center()[1] * UP + UP) From 72efc2cad9590837a615bcba81515b1e5e067470 Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Fri, 23 Feb 2018 18:36:54 +0100 Subject: [PATCH 08/11] classes ArcBetweenPoints, CurvedArrow, CurvedDoubleArrow This makes it easier to draw arcs --- topics/geometry.py | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/topics/geometry.py b/topics/geometry.py index ec1b3f73..92bb07bb 100644 --- a/topics/geometry.py +++ b/topics/geometry.py @@ -96,6 +96,47 @@ class Arc(VMobject): return self + + +class ArcBetweenPoints(Arc): + + def __init__(self, start_point, end_point, angle = TAU/4, **kwargs): + if angle == 0: + raise Exception("Arc with zero curve angle. Use Line instead.") + + midpoint = 0.5 * (start_point + end_point) + distance_vector = end_point - start_point + normal_vector = np.array([-distance_vector[1], distance_vector[0],0]) + distance = np.linalg.norm(normal_vector) + normal_vector /= distance + radius = distance/2 / np.sin(0.5 * angle) + l = distance/2 / np.tan(0.5 * angle) + arc_center = midpoint + l * normal_vector + w = start_point - arc_center + if w[0] != 0: + start_angle = np.arctan(w[1]/w[0]) + else: + start_angle = np.pi/2 + + Arc.__init__(self, angle, + radius = radius, + start_angle = start_angle, + **kwargs) + self.move_arc_center_to(arc_center) + +class CurvedArrow(ArcBetweenPoints): + + def __init__(self, start_point, end_point, angle = TAU/4, **kwargs): + ArcBetweenPoints.__init__(self, start_point, end_point, angle = TAU/4, **kwargs) + self.add_tip() + +class CurvedDoubleArrow(ArcBetweenPoints): + + def __init__(self, start_point, end_point, angle = TAU/4, **kwargs): + ArcBetweenPoints.__init__(self, start_point, end_point, angle = TAU/4, **kwargs) + self.add_tip(at_start = True, at_end = True) + + class Circle(Arc): CONFIG = { "color" : RED, From 497961082e3e1d31f1d27c4ff2eb7d66e4f86a8e Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Fri, 23 Feb 2018 19:27:59 +0100 Subject: [PATCH 09/11] curved arcs can now handle negative angles --- topics/geometry.py | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/topics/geometry.py b/topics/geometry.py index 92bb07bb..ff157215 100644 --- a/topics/geometry.py +++ b/topics/geometry.py @@ -102,19 +102,22 @@ class ArcBetweenPoints(Arc): def __init__(self, start_point, end_point, angle = TAU/4, **kwargs): if angle == 0: - raise Exception("Arc with zero curve angle. Use Line instead.") + raise Exception("Arc with zero curve angle: use Line instead.") midpoint = 0.5 * (start_point + end_point) distance_vector = end_point - start_point normal_vector = np.array([-distance_vector[1], distance_vector[0],0]) distance = np.linalg.norm(normal_vector) normal_vector /= distance - radius = distance/2 / np.sin(0.5 * angle) - l = distance/2 / np.tan(0.5 * angle) + if angle < 0: + normal_vector *= -1 + + radius = distance/2 / np.sin(0.5 * np.abs(angle)) + l = distance/2 / np.tan(0.5 * np.abs(angle)) arc_center = midpoint + l * normal_vector w = start_point - arc_center if w[0] != 0: - start_angle = np.arctan(w[1]/w[0]) + start_angle = np.arctan2(w[1],w[0]) else: start_angle = np.pi/2 @@ -122,18 +125,25 @@ class ArcBetweenPoints(Arc): radius = radius, start_angle = start_angle, **kwargs) + self.move_arc_center_to(arc_center) class CurvedArrow(ArcBetweenPoints): def __init__(self, start_point, end_point, angle = TAU/4, **kwargs): - ArcBetweenPoints.__init__(self, start_point, end_point, angle = TAU/4, **kwargs) - self.add_tip() + # I know this is in reverse, but it works + if angle >= 0: + ArcBetweenPoints.__init__(self, start_point, end_point, angle = angle, **kwargs) + self.add_tip(at_start = True, at_end = False) + else: + ArcBetweenPoints.__init__(self, end_point, start_point, angle = -angle, **kwargs) + self.add_tip(at_start = False, at_end = True) + class CurvedDoubleArrow(ArcBetweenPoints): def __init__(self, start_point, end_point, angle = TAU/4, **kwargs): - ArcBetweenPoints.__init__(self, start_point, end_point, angle = TAU/4, **kwargs) + ArcBetweenPoints.__init__(self, start_point, end_point, angle = angle, **kwargs) self.add_tip(at_start = True, at_end = True) From f5e545e3dd1228785f3ecd22d6ad80774cc007fe Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Fri, 23 Feb 2018 19:38:27 +0100 Subject: [PATCH 10/11] finished IntroScene tweaks --- active_projects/basel.py | 111 ++++++++++++++++++++++++++++++--------- 1 file changed, 87 insertions(+), 24 deletions(-) diff --git a/active_projects/basel.py b/active_projects/basel.py index 1f3b49f6..ff8f4316 100644 --- a/active_projects/basel.py +++ b/active_projects/basel.py @@ -241,8 +241,8 @@ class IntroScene(PiCreatureScene): CONFIG = { "rect_height" : 0.2, - "duration" : 1.0, - "eq_spacing" : 3 * MED_LARGE_BUFF + "duration" : 0.5, + "eq_spacing" : 6 * MED_LARGE_BUFF } def construct(self): @@ -328,6 +328,8 @@ class IntroScene(PiCreatureScene): ReplacementTransform(self.partial_sum_decimal, self.q_marks) ) + self.wait() + def build_up_sum_on_number_line(self): @@ -342,21 +344,25 @@ class IntroScene(PiCreatureScene): unit_size = 5, tick_frequency = 0.2, line_to_number_buff = MED_LARGE_BUFF - ) + ).shift(LEFT) self.number_line_labels = self.number_line.get_number_mobjects() - self.add(self.number_line,self.number_line_labels) + self.play( + FadeIn(self.number_line), + FadeIn(self.number_line_labels) + ) self.wait() # create slabs for series terms - max_n = 10 + max_n1 = 10 + max_n2 = 100 - terms = [0] + [1./(n**2) for n in range(1, max_n + 1)] + terms = [0] + [1./(n**2) for n in range(1, max_n2 + 1)] series_terms = np.cumsum(terms) lines = VGroup() self.rects = VGroup() - slab_colors = [YELLOW, BLUE] * (max_n / 2) + slab_colors = [YELLOW, BLUE] * (max_n2 / 2) for t1, t2, color in zip(series_terms, series_terms[1:], slab_colors): line = Line(*map(self.number_line.number_to_point, [t1, t2])) @@ -375,52 +381,102 @@ class IntroScene(PiCreatureScene): #self.rects.radial_gradient_highlight(ORIGIN, 5, YELLOW, BLUE) + self.little_euler_terms = VGroup() + for i in range(1,7): + if i == 1: + term = TexMobject("1", fill_color = slab_colors[i-1]) + else: + term = TexMobject("{1\over " + str(i**2) + "}", fill_color = slab_colors[i-1]) + term.scale(0.4) + self.little_euler_terms.add(term) + + for i in range(5): self.play( GrowFromPoint(self.rects[i], self.euler_sum[2*i].get_center(), - run_time = self.duration) + run_time = 1) ) + term = self.little_euler_terms.submobjects[i] + term.next_to(self.rects[i], UP) + self.play(FadeIn(term)) - for i in range(5, max_n): + self.ellipsis = TexMobject("\cdots") + self.ellipsis.scale(0.4) + + for i in range(5, max_n1): + + if i == 5: + self.ellipsis.next_to(self.rects[i+3], UP) + self.play( + FadeIn(self.ellipsis), + GrowFromPoint(self.rects[i], self.euler_sum[10].get_center(), + run_time = 0.5) + ) + else: + self.play( + GrowFromPoint(self.rects[i], self.euler_sum[10].get_center(), + run_time = 0.5) + ) + + for i in range(max_n1, max_n2): self.play( - GrowFromPoint(self.rects[i], self.euler_sum[10].get_center(), - run_time = self.duration) - ) + GrowFromPoint(self.rects[i], self.euler_sum[10].get_center(), + run_time = 0.01) + ) + + self.wait() + + PI = TAU/2 + P = self.q_marks.get_center() + 0.5 * DOWN + 0.5 * LEFT + Q = self.rects[-1].get_center() + 0.2 * UP + self.arrow = CurvedArrow(P, Q, + angle = TAU/12, + color = YELLOW + ) + + self.play(FadeIn(self.arrow)) + + self.wait() def show_pi_answer(self): self.pi_answer = TexMobject("{\\pi^2 \\over 6}").highlight(YELLOW) self.pi_answer.move_to(self.partial_sum_decimal) - self.pi_answer.next_to(self.euler_sum[-1], RIGHT, + self.pi_answer.next_to(self.euler_sum[-1], RIGHT, buff = 1, submobject_to_align = self.pi_answer[-2]) self.play(ReplacementTransform(self.q_marks, self.pi_answer)) + self.wait() + def other_pi_formulas(self): self.play( FadeOut(self.rects), FadeOut(self.number_line_labels), - FadeOut(self.number_line) + FadeOut(self.number_line), + FadeOut(self.little_euler_terms), + FadeOut(self.ellipsis), + FadeOut(self.arrow) ) self.leibniz_sum = TexMobject( "1-{1\\over 3}+{1\\over 5}-{1\\over 7}+{1\\over 9}-\\cdots", - "=", "{\\pi \\over 4}") + "=", "\quad\,\,{\\pi \\over 4}", arg_separator = " \\, ") self.wallis_product = TexMobject( "{2\\over 1} \\cdot {2\\over 3} \\cdot {4\\over 3} \\cdot {4\\over 5}" + "\\cdot {6\\over 5} \\cdot {6\\over 7} \\cdots", - "=", "{\\pi \\over 2}") + "=", "\quad\,\, {\\pi \\over 2}", arg_separator = " \\, ") self.leibniz_sum.next_to(self.euler_sum.get_part_by_tex("="), DOWN, - buff = self.eq_spacing, + buff = 2, submobject_to_align = self.leibniz_sum.get_part_by_tex("=") ) self.wallis_product.next_to(self.leibniz_sum.get_part_by_tex("="), DOWN, - buff = self.eq_spacing, + buff = 2, submobject_to_align = self.wallis_product.get_part_by_tex("=") ) @@ -448,7 +504,11 @@ class IntroScene(PiCreatureScene): # focus on pi squared pi_squared = self.euler_sum.get_part_by_tex("\\pi")[-3] self.play( - ScaleInPlace(pi_squared,2,rate_func = wiggle) + WiggleOutThenIn(pi_squared, + scale_value = 4, + angle = 0.003 * TAU, + run_time = 2 + ) ) @@ -458,17 +518,20 @@ class IntroScene(PiCreatureScene): q_circle = Circle( stroke_color = YELLOW, fill_color = YELLOW, - fill_opacity = 0.5, - radius = 0.4, - stroke_width = 10.0 + fill_opacity = 0.25, + radius = 0.5, + stroke_width = 3.0 ) q_mark = TexMobject("?") q_mark.next_to(q_circle) thought = Group(q_circle, q_mark) - q_mark.scale_to_fit_height(0.8 * q_circle.get_height()) + q_mark.scale_to_fit_height(0.6 * q_circle.get_height()) + + self.look_at(pi_squared) self.pi_creature_thinks(thought,target_mode = "confused", - bubble_kwargs = { "height" : 2, "width" : 3 }) + bubble_kwargs = { "height" : 2.5, "width" : 5 }) + self.look_at(pi_squared) self.wait() From 123b45298afeccad5b04f374191566ec5bedda92 Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Mon, 26 Feb 2018 18:39:33 +0100 Subject: [PATCH 11/11] microedits --- active_projects/basel.py | 108 ++++++++++++++++++++++++++++----------- topics/light.py | 18 ++++--- 2 files changed, 87 insertions(+), 39 deletions(-) diff --git a/active_projects/basel.py b/active_projects/basel.py index ff8f4316..d85ee926 100644 --- a/active_projects/basel.py +++ b/active_projects/basel.py @@ -18,7 +18,6 @@ from topics.characters import * from topics.functions import * from topics.number_line import * from topics.numerals import * -#from topics.combinatorics import * from scene import Scene from camera import Camera from mobject.svg_mobject import * @@ -40,14 +39,19 @@ FAST_INDICATOR_UPDATE_TIME = 0.1 OPACITY_FOR_UNIT_INTENSITY = 0.2 SWITCH_ON_RUN_TIME = 1.5 FAST_SWITCH_ON_RUN_TIME = 0.1 -NUM_LEVELS = 30 NUM_CONES = 7 # in first lighthouse scene NUM_VISIBLE_CONES = 5 # ibidem ARC_TIP_LENGTH = 0.2 -AMBIENT_FULL = 0.5 -AMBIENT_DIMMED = 0.2 -SPOTLIGHT_FULL = 0.9 + +NUM_LEVELS = 20 +AMBIENT_FULL = 0.8 +AMBIENT_DIMMED = 0.5 +AMBIENT_SCALE = 1.0 +AMBIENT_RADIUS = 20.0 +SPOTLIGHT_FULL = 0.8 SPOTLIGHT_DIMMED = 0.2 +SPOTLIGHT_SCALE = 1.0 +SPOTLIGHT_RADIUS = 20.0 LIGHT_COLOR = YELLOW DEGREES = TAU/360 @@ -373,7 +377,7 @@ class IntroScene(PiCreatureScene): rect.stretch_to_fit_height( self.rect_height, ) - rect.stretch_to_fit_width(line.get_width()) + rect.stretch_to_fit_width(0.5 * line.get_width()) rect.move_to(line) self.rects.add(rect) @@ -604,7 +608,7 @@ class FirstLighthouseScene(PiCreatureScene): width = 2.5, height = 3.5) bubble.next_to(randy,LEFT+UP) bubble.add_content(light_indicator) - + self.wait() self.play( randy.change, "wave_2", ShowCreation(bubble), @@ -629,15 +633,15 @@ class FirstLighthouseScene(PiCreatureScene): for i in range(1,NUM_CONES+1): light_source = LightSource( - opacity_function = inverse_quadratic(1,2,1), + opacity_function = inverse_quadratic(1,AMBIENT_SCALE,1), num_levels = NUM_LEVELS, - radius = 12.0, + radius = AMBIENT_RADIUS, ) point = self.number_line.number_to_point(i) light_source.move_source_to(point) light_sources.append(light_source) - + self.wait() for ls in light_sources: self.add_foreground_mobject(ls.lighthouse) @@ -651,7 +655,7 @@ class FirstLighthouseScene(PiCreatureScene): # slowly switch on visible light cones and increment indicator for (i,light_source) in zip(range(NUM_VISIBLE_CONES),light_sources[:NUM_VISIBLE_CONES]): - indicator_start_time = 0.4 * (i+1) * SWITCH_ON_RUN_TIME/light_source.radius * self.number_line.unit_size + indicator_start_time = 1.0 * (i+1) * SWITCH_ON_RUN_TIME/light_source.radius * self.number_line.unit_size indicator_stop_time = indicator_start_time + INDICATOR_UPDATE_TIME indicator_rate_func = squish_rate_func( smooth,indicator_start_time,indicator_stop_time) @@ -664,10 +668,12 @@ class FirstLighthouseScene(PiCreatureScene): # this last line *technically* fades in the last term, but it is off-screen ChangeDecimalToValue(light_indicator.reading,intensities[i], rate_func = indicator_rate_func, run_time = SWITCH_ON_RUN_TIME), - ApplyMethod(light_indicator.foreground.set_fill,None,opacities[i]) + ApplyMethod(light_indicator.foreground.set_fill,None,opacities[i], + rate_func = indicator_rate_func, run_time = SWITCH_ON_RUN_TIME) ) if i == 0: + self.wait() # move a copy out of the thought bubble for comparison light_indicator_copy = light_indicator.copy() old_y = light_indicator_copy.get_center()[1] @@ -676,6 +682,10 @@ class FirstLighthouseScene(PiCreatureScene): light_indicator_copy.shift,[0, new_y - old_y,0] ) + self.wait() + + self.wait() + # quickly switch on off-screen light cones and increment indicator for (i,light_source) in zip(range(NUM_VISIBLE_CONES,NUM_CONES),light_sources[NUM_VISIBLE_CONES:NUM_CONES]): indicator_start_time = 0.5 * (i+1) * FAST_SWITCH_ON_RUN_TIME/light_source.radius * self.number_line.unit_size @@ -754,10 +764,12 @@ class SingleLighthouseScene(PiCreatureScene): # Light source self.light_source = LightSource( - opacity_function = inverse_quadratic(1,2,1), + opacity_function = inverse_quadratic(1,SPOTLIGHT_SCALE,1), num_levels = NUM_LEVELS, radius = 10, - max_opacity_ambient = AMBIENT_FULL + max_opacity_ambient = AMBIENT_FULL, + max_opacity_spotlight = SPOTLIGHT_FULL, + ) self.light_source.move_source_to(source_point) @@ -780,7 +792,7 @@ class SingleLighthouseScene(PiCreatureScene): # Screen self.screen = Rectangle( - width = 0.1, + width = 0.06, height = 2, mark_paths_closed = True, fill_color = WHITE, @@ -797,26 +809,39 @@ class SingleLighthouseScene(PiCreatureScene): self.play(FadeIn(self.screen)) - self.light_source.set_max_opacity_spotlight(0.001) - self.add(self.light_source.spotlight) + #self.light_source.set_max_opacity_spotlight(0.001) + #self.play(SwitchOn(self.light_source.spotlight)) - self.screen_tracker = ScreenTracker(self.light_source) - self.add(self.screen_tracker) self.wait() + + # just calling .dim_ambient via ApplyMethod does not work, why? dimmed_ambient_light = self.light_source.ambient_light.deepcopy() dimmed_ambient_light.dimming(AMBIENT_DIMMED) + self.light_source.update_shadow() self.play( - Transform(self.light_source.ambient_light,dimmed_ambient_light), - self.light_source.set_max_opacity_spotlight,1.0, - FadeIn(self.light_source.shadow) + FadeIn(self.light_source.shadow), + ) + self.add_foreground_mobject(self.light_source.shadow) + self.add_foreground_mobject(morty) + + self.play( + self.light_source.dim_ambient, + #Transform(self.light_source.ambient_light,dimmed_ambient_light), + #self.light_source.set_max_opacity_spotlight,1.0, + ) + self.play( + FadeIn(self.light_source.spotlight) ) - self.add_foreground_mobject(morty) + self.screen_tracker = ScreenTracker(self.light_source) + self.add(self.screen_tracker) + + self.wait() @@ -843,7 +868,9 @@ class SingleLighthouseScene(PiCreatureScene): self.angle_indicator = DecimalNumber(arc_angle / DEGREES, num_decimal_points = 0, - unit = "^\\circ") + unit = "^\\circ", + fill_opacity = 1.0, + fill_color = WHITE) self.angle_indicator.next_to(self.angle_arc,RIGHT) angle_update_func = lambda x: self.light_source.spotlight.opening_angle() / DEGREES @@ -866,6 +893,7 @@ class SingleLighthouseScene(PiCreatureScene): self.play(Rotate(self.light_source.spotlight.screen, TAU/8)) self.play(Rotate(self.light_source.spotlight.screen, -TAU/4)) + self.play(Rotate(self.light_source.spotlight.screen, TAU/8)) self.wait() @@ -891,6 +919,7 @@ class SingleLighthouseScene(PiCreatureScene): FadeOut(self.angle_arc), FadeOut(self.angle_indicator) ) + self.wait() self.sun = self.light_source.deepcopy() @@ -904,9 +933,10 @@ class SingleLighthouseScene(PiCreatureScene): self.sun.set_radius(150) self.sun.move_source_to(sun_position) - # self.sun.update() + #self.sun.update() - # self.add(self.sun) + #self.add(self.sun) + self.wait() # temporarily remove the screen tracker while we move the source #self.remove(self.screen_tracker) @@ -951,8 +981,8 @@ class EarthScene(Scene): # screen self.screen = VMobject(stroke_color = WHITE, stroke_width = SCREEN_THICKNESS) self.screen.set_points_as_corners([ - [3,-self.screen_height/2,0], - [3,self.screen_height/2,0] + [0,-self.screen_height/2,0], + [0,self.screen_height/2,0] ]) # Earth @@ -961,6 +991,7 @@ class EarthScene(Scene): earth_center = [earth_center_x,0,0] earth_radius = 3 earth = Circle(radius = earth_radius) + earth.add(self.screen) earth.move_to(earth_center) #self.remove(self.screen_tracker) @@ -969,8 +1000,15 @@ class EarthScene(Scene): theta1 = theta0 + dtheta theta = (theta0 + theta1)/2 - earth.add(self.screen) + self.add_foreground_mobject(self.screen) + # background Earth + background_earth = SVGMobject( + file_name = "earth", + width = 2 * earth_radius, + fill_color = BLUE, + ) + background_earth.move_to(earth_center) # Morty morty = Mortimer().scale(0.5).next_to(self.screen, RIGHT, buff = 1.5) @@ -1003,8 +1041,13 @@ class EarthScene(Scene): self.wait() - self.play(FadeIn(earth)) - self.bring_to_back(earth) + self.play( + FadeIn(earth), + FadeIn(background_earth) + ) + self.add_foreground_mobject(earth) + self.add_foreground_mobject(self.screen) + # move screen onto Earth screen_on_earth = self.screen.deepcopy() @@ -1016,6 +1059,7 @@ class EarthScene(Scene): 0])) polar_morty = morty.copy().scale(0.5).next_to(screen_on_earth,DOWN,buff = 0.5) + polar_morty.highlight(BLUE_C) self.play( Transform(self.screen, screen_on_earth), @@ -1027,6 +1071,8 @@ class EarthScene(Scene): tropical_morty = polar_morty.copy() tropical_morty.move_to(np.array([0,0,0])) + tropical_morty.highlight(RED) + morty.target = tropical_morty # move screen to equator diff --git a/topics/light.py b/topics/light.py index 3bbcd9e5..c24662a5 100644 --- a/topics/light.py +++ b/topics/light.py @@ -29,10 +29,10 @@ NUM_LEVELS = 30 NUM_CONES = 7 # in first lighthouse scene NUM_VISIBLE_CONES = 5 # ibidem ARC_TIP_LENGTH = 0.2 -AMBIENT_FULL = 0.5 -AMBIENT_DIMMED = 0.2 -SPOTLIGHT_FULL = 0.9 -SPOTLIGHT_DIMMED = 0.2 +AMBIENT_FULL = 0.8 +AMBIENT_DIMMED = 0.5 +SPOTLIGHT_FULL = 0.8 +SPOTLIGHT_DIMMED = 0.5 LIGHTHOUSE_HEIGHT = 0.8 DEGREES = TAU/360 @@ -57,7 +57,7 @@ class LightSource(VMobject): "source_point": VectorizedPoint(location = ORIGIN, stroke_width = 0, fill_opacity = 0), "color": LIGHT_COLOR, "num_levels": 10, - "radius": 5, + "radius": 10.0, "screen": None, "opacity_function": inverse_quadratic(1,2,1), "max_opacity_ambient": AMBIENT_FULL, @@ -143,7 +143,9 @@ class LightSource(VMobject): num_levels = self.num_levels, radius = self.radius, screen = new_screen, - camera_mob = self.camera_mob + camera_mob = self.camera_mob, + opacity_function = self.opacity_function, + max_opacity = self.max_opacity_spotlight, ) self.spotlight.move_source_to(self.get_source_point()) @@ -371,7 +373,7 @@ class AmbientLight(VMobject): "color" : LIGHT_COLOR, "max_opacity" : 1.0, "num_levels" : 10, - "radius" : 5.0 + "radius" : 10.0 } def generate_points(self): @@ -452,7 +454,7 @@ class Spotlight(VMobject): "color" : GREEN, # LIGHT_COLOR, "max_opacity" : 1.0, "num_levels" : 10, - "radius" : 5.0, + "radius" : 10.0, "screen" : None, "camera_mob": None }