Added units to DecimalNumer

This commit is contained in:
Ben Hambrecht 2018-01-23 10:53:24 -08:00
parent c7f92d53cd
commit 66e001d77a
3 changed files with 181 additions and 44 deletions

View file

@ -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])
)

View file

@ -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

View file

@ -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())