From b683b5f62848b58fa8c4494f01d119d7ce919358 Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Mon, 22 Jan 2018 11:58:04 -0800 Subject: [PATCH 1/7] rotate_in_place changes --- mobject/mobject.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mobject/mobject.py b/mobject/mobject.py index 84f113d5..6e135c95 100644 --- a/mobject/mobject.py +++ b/mobject/mobject.py @@ -248,9 +248,9 @@ class Mobject(object): mob.points += about_point return self - def rotate_in_place(self, angle, axis = OUT, axes = []): + def rotate_in_place(self, angle, axis = OUT): # redundant with default behavior of rotate now. - return self.rotate(angle, axis = axis, axes = axes) + return self.rotate(angle, axis = axis) def scale_in_place(self, scale_factor, **kwargs): #Redundant with default behavior of scale now. From 75fcb30e7771600323d3acd06ae586e636a66cb4 Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Mon, 22 Jan 2018 14:41:05 -0800 Subject: [PATCH 2/7] Renaming line_to_number_vect -> label_direction --- topics/number_line.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/topics/number_line.py b/topics/number_line.py index e5ced347..7aa5fa9b 100644 --- a/topics/number_line.py +++ b/topics/number_line.py @@ -21,11 +21,12 @@ class NumberLine(VMobject): "longer_tick_multiple" : 2, "number_at_center" : 0, "number_scale_val" : 0.75, - "line_to_number_vect" : DOWN, + "label_direction" : DOWN, "line_to_number_buff" : MED_SMALL_BUFF, "include_tip" : False, "propagate_style_to_family" : True, } + def __init__(self, **kwargs): digest_config(self, kwargs) if self.leftmost_tick is None: @@ -113,7 +114,7 @@ class NumberLine(VMobject): mob.scale(self.number_scale_val) mob.next_to( self.number_to_point(number), - self.line_to_number_vect, + self.label_direction, self.line_to_number_buff, ) result.add(mob) @@ -135,6 +136,8 @@ class NumberLine(VMobject): self.tip = tip self.add(tip) + + class UnitInterval(NumberLine): CONFIG = { "x_min" : 0, @@ -386,4 +389,3 @@ class NumberPlane(VMobject): - From 2eb6f7f6146ca599ed9dd7bc54d7dcbc704aa031 Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Mon, 22 Jan 2018 14:41:45 -0800 Subject: [PATCH 3/7] Improved first lighthouse scene (now with series on top) --- active_projects/basel.py | 81 +++++++++++++++++++++++++++++++++------- 1 file changed, 68 insertions(+), 13 deletions(-) diff --git a/active_projects/basel.py b/active_projects/basel.py index afe564b6..298b34aa 100644 --- a/active_projects/basel.py +++ b/active_projects/basel.py @@ -40,9 +40,13 @@ INDICATOR_STROKE_WIDTH = 1 INDICATOR_STROKE_COLOR = WHITE INDICATOR_TEXT_COLOR = WHITE INDICATOR_UPDATE_TIME = 0.2 +FAST_INDICATOR_UPDATE_TIME = 0.1 OPACITY_FOR_UNIT_INTENSITY = 0.2 SWITCH_ON_RUN_TIME = 2.0 -LIGHT_CONE_NUM_SECTORS = 50 +FAST_SWITCH_ON_RUN_TIME = 0.1 +LIGHT_CONE_NUM_SECTORS = 10 +NUM_CONES = 10 +NUM_VISIBLE_CONES = 6 @@ -506,13 +510,16 @@ class FirstLightHouseScene(PiCreatureScene): color = WHITE, number_at_center = 1.6, stroke_width = 1, - numbers_with_elongated_ticks = [0,1,2,3], - numbers_to_show = np.arange(1,5), + numbers_with_elongated_ticks = range(1,5), + numbers_to_show = range(1,5), unit_size = 2, tick_frequency = 0.2, - line_to_number_buff = LARGE_BUFF + line_to_number_buff = LARGE_BUFF, + label_direction = UP, ) + self.number_line.label_direction = DOWN + self.number_line_labels = self.number_line.get_number_mobjects() self.add(self.number_line,self.number_line_labels) self.wait() @@ -549,9 +556,22 @@ class FirstLightHouseScene(PiCreatureScene): lighthouse_pos = [] light_cones = [] - num_cones = 6 - for i in range(1,num_cones+1): + euler_sum_above = TexMobject("1", "+", "{1\over 4}", + "+", "{1\over 9}", "+", "{1\over 16}", "+", "{1\over 25 }", "+", "{1\over 36}") + euler_sum_above.fill_color = YELLOW + + for (i,term) in zip(range(len(euler_sum_above)),euler_sum_above): + #horizontal alignment with tick marks + term.next_to(self.number_line.number_to_point(0.5*i+1),UP,buff = 2) + # vertical alignment with light indicator + old_y = term.get_center()[1] + new_y = light_indicator.get_center()[1] + term.shift([0,new_y - old_y,0]) + + + + for i in range(1,NUM_CONES+1): lighthouse = LightHouse() point = self.number_line.number_to_point(i) light_cone = Candle( @@ -571,33 +591,64 @@ class FirstLightHouseScene(PiCreatureScene): light_indicator.set_intensity(0) - intensities = np.cumsum(np.array([1./n**2 for n in range(1,num_cones+1)])) + intensities = np.cumsum(np.array([1./n**2 for n in range(1,NUM_CONES+1)])) opacities = intensities * light_indicator.opacity_for_unit_intensity self.remove_foreground_mobjects(light_indicator) - - - for (i,lc) in zip(range(num_cones),light_cones): + # slowly switch on visible light cones and increment indicator + for (i,lc) in zip(range(NUM_VISIBLE_CONES),light_cones[:NUM_VISIBLE_CONES]): + print i indicator_start_time = 0.5 * (i+1) * SWITCH_ON_RUN_TIME/lc.radius * self.number_line.unit_size indicator_stop_time = indicator_start_time + INDICATOR_UPDATE_TIME indicator_rate_func = squish_rate_func(#smooth, 0.8, 0.9) smooth,indicator_start_time,indicator_stop_time) self.play( SwitchOn(lc), + FadeIn(euler_sum_above[2*i], run_time = SWITCH_ON_RUN_TIME, + rate_func = indicator_rate_func), + FadeIn(euler_sum_above[2*i - 1], run_time = SWITCH_ON_RUN_TIME, + rate_func = indicator_rate_func), 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]) ) if i == 0: - # mvoe a copy out of the thought bubble for comparison + # move a copy out of the thought bubble for comparison light_indicator_copy = light_indicator.copy() + old_y = light_indicator_copy.get_center()[1] + new_y = self.number_line.get_center()[1] self.play( - light_indicator_copy.shift,[2,0,0] + light_indicator_copy.shift,[0, new_y - old_y,0] ) + print "fast now" + + # quickly switch on off-screen light cones and increment indicator + for (i,lc) in zip(range(NUM_VISIBLE_CONES,NUM_CONES),light_cones[NUM_VISIBLE_CONES:NUM_CONES]): + print i + indicator_start_time = 0.5 * (i+1) * FAST_SWITCH_ON_RUN_TIME/lc.radius * self.number_line.unit_size + indicator_stop_time = indicator_start_time + FAST_INDICATOR_UPDATE_TIME + indicator_rate_func = squish_rate_func(#smooth, 0.8, 0.9) + smooth,indicator_start_time,indicator_stop_time) + self.play( + SwitchOn(lc, run_time = FAST_SWITCH_ON_RUN_TIME), + ChangeDecimalToValue(light_indicator.reading,intensities[i], + rate_func = indicator_rate_func, run_time = FAST_SWITCH_ON_RUN_TIME), + ApplyMethod(light_indicator.foreground.set_fill,None,opacities[i]) + ) + + + # show limit value in light indicator and an equals sign + limit_reading = TexMobject("{\pi^2 \over 6}") + limit_reading.move_to(light_indicator.reading) + self.play( + FadeOut(indicator.reading), + FadeIn(limit_reading) +# Transform(light_indicator.reading,limit_reading) + ) @@ -622,7 +673,7 @@ class SingleLightHouseScene(PiCreatureScene): lighthouse = LightHouse() candle = Candle( - opacity_function = inverse_quadratic(0.3,1), + opacity_function = inverse_quadratic(1,1), num_sectors = LIGHT_CONE_NUM_SECTORS, radius = 10 ) @@ -650,6 +701,10 @@ class SingleLightHouseScene(PiCreatureScene): light_screen.screen.fill_opacity = 1 light_screen.update_light_cone(light_cone) self.add(light_screen) + # dim the light that misses the screen + self.play( + ApplyMethod(light_cone.set_intensity,0.3) + ) lc_updater = lambda lc: light_screen.update_light_cone(lc) sh_updater = lambda sh: light_screen.update_shadow(sh) From 1d6d3b37173b951e808dbe292135ab54fdadb15c Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Mon, 22 Jan 2018 16:01:33 -0800 Subject: [PATCH 4/7] Added methods for centering Arcs and Sectors and for their stop_angle --- topics/geometry.py | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/topics/geometry.py b/topics/geometry.py index b67d93f1..080ca212 100644 --- a/topics/geometry.py +++ b/topics/geometry.py @@ -48,6 +48,27 @@ class Arc(VMobject): self.highlight(self.get_color()) return self + + + def get_arc_center(self): + first_point = self.points[0] + radial_unit_vector = np.array([np.cos(self.start_angle),np.sin(self.start_angle),0]) + arc_center = first_point - self.radius * radial_unit_vector + return arc_center + + + def move_arc_center_to(self,point): + v = point - self.get_arc_center() + self.shift(v) + return self + + + def stop_angle(self): + return self.start_angle + self.angle + + + + class Circle(Arc): CONFIG = { "color" : RED, @@ -120,15 +141,12 @@ class AnnularSector(VMobject): v = last_point - first_point radial_unit_vector = v/np.linalg.norm(v) arc_center = first_point - self.inner_radius * radial_unit_vector - - - - # radial_unit_vector = np.array([np.cos(self.start_angle), - # np.sin(self.start_angle), 0]) - # arc_center = inner_arc_start_point - inner_arc.radius * radial_unit_vector return arc_center - + def move_arc_center_to(self,point): + v = point - self.get_arc_center() + self.shift(v) + return self @@ -171,7 +189,7 @@ class Annulus(Circle): class Line(VMobject): CONFIG = { "buff" : 0, - "path_arc" : None, + "path_arc" : None, # angle of arc specified here "n_arc_anchors" : 10, #Only used if path_arc is not None } def __init__(self, start, end, **kwargs): From 6e1c84a30512dbf8311902cd197c63af5795bdd8 Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Mon, 22 Jan 2018 16:07:53 -0800 Subject: [PATCH 5/7] Arcs can now have tips on either side Todo: tips on both sides, eliminate overshoot --- topics/geometry.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/topics/geometry.py b/topics/geometry.py index 080ca212..73318cc7 100644 --- a/topics/geometry.py +++ b/topics/geometry.py @@ -36,9 +36,12 @@ class Arc(VMobject): ) self.scale(self.radius, about_point = ORIGIN) - def add_tip(self, tip_length = 0.25): + def add_tip(self, tip_length = 0.25, end = 0): #TODO, do this a better way - p1, p2 = self.points[-2:] + if end == 1: + p1, p2 = self.points[-2:] + else: + p2, p1 = self.points[:2] arrow = Arrow( p1, 2*p2 - p1, tip_length = tip_length, From c7f92d53cd34a8ca15f2d97c8c6c208137b14f78 Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Mon, 22 Jan 2018 16:30:07 -0800 Subject: [PATCH 6/7] Arcs can now have arrows on both sides --- topics/geometry.py | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/topics/geometry.py b/topics/geometry.py index 73318cc7..a91d23e7 100644 --- a/topics/geometry.py +++ b/topics/geometry.py @@ -36,18 +36,32 @@ class Arc(VMobject): ) self.scale(self.radius, about_point = ORIGIN) - def add_tip(self, tip_length = 0.25, end = 0): + def add_tip(self, tip_length = 0.25, at_start = False, at_end = True): #TODO, do this a better way - if end == 1: + p1 = p2 = p3 = p4 = None + start_arrow = end_arrow = None + if at_start: p1, p2 = self.points[-2:] - else: - p2, p1 = self.points[:2] - arrow = Arrow( - p1, 2*p2 - p1, - tip_length = tip_length, - max_tip_length_to_length_ratio = 2.0 - ) - self.add(arrow.split()[-1]) + start_arrow = Arrow( + p1, 2*p2 - p1, + tip_length = tip_length, + max_tip_length_to_length_ratio = 2.0 + ) + self.add(start_arrow.split()[-1]) + + if at_end: + p4, p3 = self.points[:2] + end_arrow = Arrow( + p3, 2*p4 - p3, + tip_length = tip_length, + max_tip_length_to_length_ratio = 2.0 + ) + self.add(end_arrow.split()[-1]) + + + + + self.highlight(self.get_color()) return self From 66e001d77a636415f27c4f29f76ae0a8f230bc25 Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Tue, 23 Jan 2018 10:53:24 -0800 Subject: [PATCH 7/7] Added units to DecimalNumer --- active_projects/basel.py | 183 ++++++++++++++++++++++++++++++--------- topics/geometry.py | 20 ++++- topics/numerals.py | 22 ++++- 3 files changed, 181 insertions(+), 44 deletions(-) diff --git a/active_projects/basel.py b/active_projects/basel.py index 298b34aa..909fd1cc 100644 --- a/active_projects/basel.py +++ b/active_projects/basel.py @@ -42,11 +42,37 @@ INDICATOR_TEXT_COLOR = WHITE INDICATOR_UPDATE_TIME = 0.2 FAST_INDICATOR_UPDATE_TIME = 0.1 OPACITY_FOR_UNIT_INTENSITY = 0.2 -SWITCH_ON_RUN_TIME = 2.0 +SWITCH_ON_RUN_TIME = 2.5 FAST_SWITCH_ON_RUN_TIME = 0.1 -LIGHT_CONE_NUM_SECTORS = 10 -NUM_CONES = 10 -NUM_VISIBLE_CONES = 6 +LIGHT_CONE_NUM_SECTORS = 30 +NUM_CONES = 50 # in first lighthouse scene +NUM_VISIBLE_CONES = 5 # ibidem +ARC_TIP_LENGTH = 0.2 + + +def show_line_length(line): + v = line.points[1] - line.points[0] + print v[0]**2 + v[1]**2 + + +class AngleUpdater(ContinualAnimation): + def __init__(self, angle_arc, lc, **kwargs): + self.angle_arc = angle_arc + self.source_point = angle_arc.get_arc_center() + self.lc = lc + #self.angle_decimal = angle_decimal + ContinualAnimation.__init__(self, self.angle_arc, **kwargs) + + def update_mobject(self, dt): + # angle arc + new_arc = self.angle_arc.copy().set_bound_angles( + start = self.lc.start_angle, + stop = self.lc.stop_angle() + ) + new_arc.generate_points() + new_arc.move_arc_center_to(self.source_point) + self.angle_arc.points = new_arc.points + self.angle_arc.add_tip(tip_length = ARC_TIP_LENGTH, at_start = True, at_end = True) @@ -99,14 +125,13 @@ class LightScreen(VMobject): ray1 = self.screen.points[0] - self.light_source ray2 = self.screen.points[-1] - self.light_source ray1 = ray1/np.linalg.norm(ray1) * 100 - ray1 = rotate_vector(ray1,TAU/16) + ray1 = rotate_vector(ray1,-TAU/16) ray2 = ray2/np.linalg.norm(ray2) * 100 - ray2 = rotate_vector(ray2,-TAU/16) + ray2 = rotate_vector(ray2,TAU/16) outpoint1 = self.screen.points[0] + ray1 outpoint2 = self.screen.points[-1] + ray2 self.shadow.add_control_points([outpoint2,outpoint1,self.screen.points[0]]) self.shadow.mark_paths_closed = True - class LightCone(VGroup): @@ -114,6 +139,7 @@ class LightCone(VGroup): "start_angle": 0, "angle" : TAU/8, "radius" : 10, + "brightness" : 1, "opacity_function" : lambda r : 1./max(r, 0.01), "num_sectors" : 10, "color": LIGHT_COLOR, @@ -130,7 +156,7 @@ class LightCone(VGroup): stroke_width = 0, stroke_color = self.color, fill_color = self.color, - fill_opacity = self.opacity_function(r1), + fill_opacity = self.brightness * self.opacity_function(r1), ) for r1, r2 in zip(radii, radii[1:]) ] @@ -160,6 +186,15 @@ class LightCone(VGroup): submob.generate_points() submob.shift(source_point - submob.get_arc_center()) + def set_brightness(self,new_brightness): + self.brightness = new_brightness + radii = np.linspace(0, self.radius, self.num_sectors+1) + for (r1,sector) in zip(radii,self.submobjects): + sector.set_fill(opacity = self.brightness * self.opacity_function(r1)) + + def stop_angle(self): + return self.start_angle + self.angle + @@ -169,13 +204,14 @@ class LightCone(VGroup): class Candle(VGroup): CONFIG = { "radius" : 5, + "brightness" : 1.0, "opacity_function" : lambda r : 1./max(r, 0.01), - "num_sectors" : 10, + "num_annuli" : 10, "color": LIGHT_COLOR, } def generate_points(self): - radii = np.linspace(0, self.radius, self.num_sectors+1) + radii = np.linspace(0, self.radius, self.num_annuli+1) annuli = [ Annulus( inner_radius = r1, @@ -183,7 +219,7 @@ class Candle(VGroup): stroke_width = 0, stroke_color = self.color, fill_color = self.color, - fill_opacity = self.opacity_function(r1), + fill_opacity = self.brightness * self.opacity_function(r1), ) for r1, r2 in zip(radii, radii[1:]) ] @@ -201,6 +237,14 @@ class Candle(VGroup): source = self.submobjects[0].get_center() self.shift(point - source) + def set_brightness(self,new_brightness): + self.brightness = new_brightness + radii = np.linspace(0, self.radius, self.num_annuli+1) + for (r1,annulus) in zip(radii,self.submobjects): + annulus.set_fill(opacity = self.brightness * self.opacity_function(r1)) + + + class SwitchOn(LaggedStart): CONFIG = { @@ -558,8 +602,7 @@ class FirstLightHouseScene(PiCreatureScene): euler_sum_above = TexMobject("1", "+", "{1\over 4}", - "+", "{1\over 9}", "+", "{1\over 16}", "+", "{1\over 25 }", "+", "{1\over 36}") - euler_sum_above.fill_color = YELLOW + "+", "{1\over 9}", "+", "{1\over 16}", "+", "{1\over 25}", "+", "{1\over 36}") for (i,term) in zip(range(len(euler_sum_above)),euler_sum_above): #horizontal alignment with tick marks @@ -568,7 +611,7 @@ class FirstLightHouseScene(PiCreatureScene): old_y = term.get_center()[1] new_y = light_indicator.get_center()[1] term.shift([0,new_y - old_y,0]) - + for i in range(1,NUM_CONES+1): @@ -576,8 +619,8 @@ class FirstLightHouseScene(PiCreatureScene): point = self.number_line.number_to_point(i) light_cone = Candle( opacity_function = inverse_quadratic(1,1), - num_sectors = LIGHT_CONE_NUM_SECTORS, - radius = 10) + num_annuli = LIGHT_CONE_NUM_SECTORS, + radius = 12) light_cone.move_source_to(point) lighthouse.next_to(point,DOWN,0) @@ -599,10 +642,9 @@ class FirstLightHouseScene(PiCreatureScene): # slowly switch on visible light cones and increment indicator for (i,lc) in zip(range(NUM_VISIBLE_CONES),light_cones[:NUM_VISIBLE_CONES]): - print i - indicator_start_time = 0.5 * (i+1) * SWITCH_ON_RUN_TIME/lc.radius * self.number_line.unit_size + indicator_start_time = 0.4 * (i+1) * SWITCH_ON_RUN_TIME/lc.radius * self.number_line.unit_size indicator_stop_time = indicator_start_time + INDICATOR_UPDATE_TIME - indicator_rate_func = squish_rate_func(#smooth, 0.8, 0.9) + indicator_rate_func = squish_rate_func( smooth,indicator_start_time,indicator_stop_time) self.play( SwitchOn(lc), @@ -624,11 +666,8 @@ class FirstLightHouseScene(PiCreatureScene): light_indicator_copy.shift,[0, new_y - old_y,0] ) - print "fast now" - # quickly switch on off-screen light cones and increment indicator for (i,lc) in zip(range(NUM_VISIBLE_CONES,NUM_CONES),light_cones[NUM_VISIBLE_CONES:NUM_CONES]): - print i indicator_start_time = 0.5 * (i+1) * FAST_SWITCH_ON_RUN_TIME/lc.radius * self.number_line.unit_size indicator_stop_time = indicator_start_time + FAST_INDICATOR_UPDATE_TIME indicator_rate_func = squish_rate_func(#smooth, 0.8, 0.9) @@ -644,10 +683,17 @@ class FirstLightHouseScene(PiCreatureScene): # show limit value in light indicator and an equals sign limit_reading = TexMobject("{\pi^2 \over 6}") limit_reading.move_to(light_indicator.reading) + + equals_sign = TexMobject("=") + equals_sign.next_to(randy, UP) + old_y = equals_sign.get_center()[1] + new_y = euler_sum_above.get_center()[1] + equals_sign.shift([0,new_y - old_y,0]) + self.play( - FadeOut(indicator.reading), - FadeIn(limit_reading) -# Transform(light_indicator.reading,limit_reading) + FadeOut(light_indicator.reading), + FadeIn(limit_reading), + FadeIn(equals_sign), ) @@ -674,36 +720,46 @@ class SingleLightHouseScene(PiCreatureScene): lighthouse = LightHouse() candle = Candle( opacity_function = inverse_quadratic(1,1), - num_sectors = LIGHT_CONE_NUM_SECTORS, - radius = 10 + num_annuli = LIGHT_CONE_NUM_SECTORS, + radius = 10, + brightness = 1, ) lighthouse.scale(2).next_to(source_point, DOWN, buff = 0) candle.move_to(source_point) morty = self.get_primary_pi_creature() morty.scale(0.5) morty.move_to(observer_point) - self.add(lighthouse, candle) - self.wait() + self.add(lighthouse) self.play( SwitchOn(candle) ) - light_cone = LightCone() + light_cone = LightCone( + opacity_function = inverse_quadratic(1,1), + num_sectors = LIGHT_CONE_NUM_SECTORS, + radius = 10, + brightness = 5, + ) light_cone.move_source_to(source_point) - screen = Arc(TAU/4).rotate_in_place(TAU/2).shift(3*RIGHT) - screen.radius = 4 - screen.start_angle = -TAU/5 - screen.next_to(morty, LEFT) + screen = Line([0,-1,0],[0,1,0]) + show_line_length(screen) + + screen.rotate_in_place(-TAU/6) + show_line_length(screen) + + screen.next_to(morty, LEFT, buff = 1) light_screen = LightScreen(light_source = source_point, screen = screen, light_cone = light_cone) light_screen.screen.color = WHITE light_screen.screen.fill_opacity = 1 light_screen.update_light_cone(light_cone) - self.add(light_screen) - # dim the light that misses the screen self.play( - ApplyMethod(light_cone.set_intensity,0.3) + FadeIn(light_screen, run_time = 2), + # dim the light that misses the screen + ApplyMethod(candle.set_brightness,0.3), + ApplyMethod(light_screen.update_shadow,light_screen.shadow), + FadeIn(light_cone), ) lc_updater = lambda lc: light_screen.update_light_cone(lc) @@ -717,9 +773,58 @@ class SingleLightHouseScene(PiCreatureScene): self.add(ca1, ca2) self.add_foreground_mobject(morty) - moving_screen = ApplyMethod(screen.move_to, [1,0,0], run_time=3) + pointing_screen_at_source = ApplyMethod(screen.rotate_in_place,TAU/6) + self.play(pointing_screen_at_source) - self.play(moving_screen) + arc_angle = light_cone.angle + # draw arc arrows to show the opening angle + angle_arc = Arc(radius = 5, start_angle = light_cone.start_angle, + angle = light_cone.angle, tip_length = ARC_TIP_LENGTH) + #angle_arc.add_tip(at_start = True, at_end = True) + angle_arc.move_arc_center_to(source_point) + + self.add(angle_arc) + + angle_indicator = DecimalNumber(arc_angle/TAU*360, + num_decimal_points = 0, + unit = "^\\circ") + angle_indicator.next_to(angle_arc,RIGHT) + self.add_foreground_mobject(angle_indicator) + + angle_update_func = lambda x: light_cone.angle/TAU*360 + ca3 = ContinualChangingDecimal(angle_indicator,angle_update_func) + self.add(ca3) + + #ca4 = ContinualUpdateFromFunc(angle_arc,update_angle_arc) + ca4 = AngleUpdater(angle_arc, light_screen.light_cone) + self.add(ca4) + + rotating_screen = ApplyMethod(light_screen.screen.rotate_in_place, TAU/6, run_time=3, rate_func = wiggle) + self.play(rotating_screen) + + #rotating_screen_back = ApplyMethod(light_screen.screen.rotate_in_place, -TAU/6) #, run_time=3, rate_func = wiggle) + #self.play(rotating_screen_back) + + self.wait() + + + + # morph into Earth scene + + globe = Circle(radius = 3) + globe.move_to([2,0,0]) + sun_position = [-100,0,0] + self.play( + ApplyMethod(lighthouse.move_to,sun_position), + ApplyMethod(candle.move_to,sun_position), + ApplyMethod(light_cone.move_source_to,sun_position), + FadeOut(angle_arc), + FadeOut(angle_indicator), + FadeIn(globe), + ApplyMethod(light_screen.move_to,[0,0,0]), + ApplyMethod(morty.move_to,[1,0,0]) + + ) diff --git a/topics/geometry.py b/topics/geometry.py index a91d23e7..57bd1778 100644 --- a/topics/geometry.py +++ b/topics/geometry.py @@ -37,20 +37,27 @@ class Arc(VMobject): self.scale(self.radius, about_point = ORIGIN) def add_tip(self, tip_length = 0.25, at_start = False, at_end = True): + # clear out any old tips + for submob in self.submobjects: + if submob.mark_paths_closed == True: # is a tip + self.remove(submob) + #TODO, do this a better way p1 = p2 = p3 = p4 = None start_arrow = end_arrow = None if at_start: - p1, p2 = self.points[-2:] + p1, p2 = self.points[-3:-1] + # self.points[-2:] did overshoot start_arrow = Arrow( p1, 2*p2 - p1, tip_length = tip_length, max_tip_length_to_length_ratio = 2.0 ) - self.add(start_arrow.split()[-1]) + self.add(start_arrow.split()[-1]) # just the tip if at_end: - p4, p3 = self.points[:2] + p4, p3 = self.points[1:3] + # self.points[:2] did overshoot end_arrow = Arrow( p3, 2*p4 - p3, tip_length = tip_length, @@ -83,6 +90,13 @@ class Arc(VMobject): def stop_angle(self): return self.start_angle + self.angle + def set_bound_angles(self,start=0,stop=np.pi): + self.start_angle = start + self.angle = stop - start + + return self + + diff --git a/topics/numerals.py b/topics/numerals.py index f8ee064f..d3601839 100644 --- a/topics/numerals.py +++ b/topics/numerals.py @@ -10,7 +10,8 @@ class DecimalNumber(VMobject): CONFIG = { "num_decimal_points" : 2, "digit_to_digit_buff" : 0.05, - "show_ellipsis" : False + "show_ellipsis" : False, + "unit" : None } def __init__(self, number, **kwargs): digest_config(self, kwargs, locals()) @@ -25,11 +26,13 @@ class DecimalNumber(VMobject): if self.show_ellipsis: self.add(TexMobject("\\dots")) + self.arrange_submobjects( buff = self.digit_to_digit_buff, aligned_edge = DOWN ) + if num_string.startswith("-"): minus = self.submobjects[0] minus.next_to( @@ -37,6 +40,20 @@ class DecimalNumber(VMobject): buff = self.digit_to_digit_buff ) + + if self.unit != None: + unit_sign = TexMobject(self.unit) + unit_sign.next_to(self.submobjects[-1],RIGHT, + buff = self.digit_to_digit_buff) + + if self.unit == "^\\circ": + unit_sign.align_to(self,UP) + else: + unit_sign.align_to(self,DOWN) + self.add(unit_sign) + + + class Integer(VGroup): CONFIG = { "digit_buff" : 0.8*SMALL_BUFF @@ -85,7 +102,8 @@ class ChangingDecimal(Animation): new_decimal = DecimalNumber( new_number, num_decimal_points = self.num_decimal_points, - show_ellipsis = self.show_ellipsis + show_ellipsis = self.show_ellipsis, + unit = self.decimal_number_mobject.unit ) new_decimal.replace(decimal, dim_to_match = 1) new_decimal.highlight(decimal.get_color())