refactoring scenes 2-5, started on scene 6 (faux 3D)

This commit is contained in:
Ben Hambrecht 2018-01-29 15:20:57 +01:00
parent e139d54ba9
commit b38a8dade6

View file

@ -50,12 +50,9 @@ NUM_VISIBLE_CONES = 5 # ibidem
ARC_TIP_LENGTH = 0.2
AMBIENT_FULL = 1.0
AMBIENT_DIMMED = 0.2
LIGHT_COLOR = YELLOW
DEGREES = TAU/360
def show_line_length(line):
v = line.points[1] - line.points[0]
print v[0]**2 + v[1]**2
SWITCH_ON_RUN_TIME = 1.5
class AngleUpdater(ContinualAnimation):
@ -79,9 +76,18 @@ class AngleUpdater(ContinualAnimation):
LIGHT_COLOR = YELLOW
DEGREES = 360/TAU
SWITCH_ON_RUN_TIME = 1.5
class AmbientLight(VMobject):
@ -214,7 +220,6 @@ class Spotlight(VMobject):
return u
def move_source_to(self,point):
print "moving source"
self.source_point = np.array(point)
self.recalculate_sectors(point = point, screen = self.screen)
self.update_shadow(point = point)
@ -375,6 +380,28 @@ class UpdateLightIndicator(AnimationGroup):
self.mobject = indicator
class IntroScene(PiCreatureScene):
CONFIG = {
@ -388,16 +415,10 @@ class IntroScene(PiCreatureScene):
randy = self.get_primary_pi_creature()
randy.scale(0.7).to_corner(DOWN+RIGHT)
self.force_skipping()
self.build_up_euler_sum()
self.build_up_sum_on_number_line()
self.show_pi_answer()
self.other_pi_formulas()
self.revert_to_original_skipping_status()
self.refocus_on_euler_sum()
@ -595,20 +616,41 @@ class IntroScene(PiCreatureScene):
ScaleInPlace(pi_squared,2,rate_func = wiggle)
)
q_circle = Circle(color=WHITE,radius=0.8)
q_mark = TexMobject("?")
q_mark.next_to(q_circle)
thought = Group(q_circle, q_mark)
q_mark.height *= 2
self.pi_creature_thinks(thought,target_mode = "confused",
bubble_kwargs = { "height" : 1.5, "width" : 2 })
self.wait()
# Morty thinks of a circle
# q_circle = Circle(color=WHITE,radius=0.8)
# q_mark = TexMobject("?")
# q_mark.next_to(q_circle)
# thought = Group(q_circle, q_mark)
# q_mark.height *= 2
# self.pi_creature_thinks(thought,target_mode = "confused",
# bubble_kwargs = { "height" : 1.5, "width" : 2 })
# self.wait()
class FirstLightHouseScene(PiCreatureScene):
class FirstLighthouseScene(PiCreatureScene):
def construct(self):
self.remove(self.get_primary_pi_creature())
@ -771,21 +813,43 @@ class FirstLightHouseScene(PiCreatureScene):
class SingleLightHouseScene(PiCreatureScene):
class SingleLighthouseScene(PiCreatureScene):
def construct(self):
self.create_light_source_and_creature()
self.setup_elements()
self.setup_trackers() # spotlight and angle msmt change when screen rotates
self.rotate_screen()
def create_light_source_and_creature(self):
def setup_elements(self):
SCREEN_SIZE = 3.0
DISTANCE_FROM_LIGHTHOUSE = 10.0
source_point = [-DISTANCE_FROM_LIGHTHOUSE/2,0,0]
observer_point = [DISTANCE_FROM_LIGHTHOUSE/2,0,0]
# Lighthouse
lighthouse = LightHouse()
ambient_light = AmbientLight(
opacity_function = inverse_quadratic(AMBIENT_FULL,2,1),
@ -795,6 +859,9 @@ class SingleLightHouseScene(PiCreatureScene):
)
lighthouse.scale(2).next_to(source_point, DOWN, buff = 0)
ambient_light.move_to(source_point)
# Pi Creature
morty = self.get_primary_pi_creature()
morty.scale(0.5)
morty.move_to(observer_point)
@ -803,72 +870,95 @@ class SingleLightHouseScene(PiCreatureScene):
SwitchOn(ambient_light)
)
# Screen
screen = Line([0,-1,0],[0,1,0])
screen.rotate(-TAU/6)
screen.next_to(morty, LEFT, buff = 1)
self.screen = Line([0,-1,0],[0,1,0])
self.screen.rotate(-TAU/6)
self.screen.next_to(morty, LEFT, buff = 1)
spotlight = Spotlight(
# Spotlight
self.spotlight = Spotlight(
opacity_function = inverse_quadratic(1,2,1),
num_levels = NUM_LEVELS,
radius = 10,
brightness = 5,
screen = screen
screen = self.screen
)
spotlight.move_source_to(source_point)
self.spotlight.move_source_to(source_point)
# Animations
self.play(
ApplyMethod(ambient_light.dimming,AMBIENT_DIMMED),
FadeIn(spotlight))
self.add(spotlight.shadow)
FadeIn(self.spotlight)
)
self.add(self.spotlight.shadow)
self.add_foreground_mobject(morty)
screen_tracker = ScreenTracker(spotlight)
# activate ONLY when spotlight is moving!
def setup_trackers(self):
# Make spotlight follow the screen
screen_tracker = ScreenTracker(self.spotlight)
self.add(screen_tracker)
pointing_screen_at_source = ApplyMethod(spotlight.screen.rotate,TAU/6)
pointing_screen_at_source = Rotate(self.screen,TAU/6)
self.play(pointing_screen_at_source)
# angle msmt (arc)
arc_angle = spotlight.opening_angle()
arc_angle = self.spotlight.opening_angle()
# draw arc arrows to show the opening angle
angle_arc = Arc(radius = 5, start_angle = spotlight.start_angle(),
angle = spotlight.opening_angle(), tip_length = ARC_TIP_LENGTH)
angle_arc = Arc(radius = 5, start_angle = self.spotlight.start_angle(),
angle = self.spotlight.opening_angle(), tip_length = ARC_TIP_LENGTH)
#angle_arc.add_tip(at_start = True, at_end = True)
angle_arc.move_arc_center_to(source_point)
angle_arc.move_arc_center_to(self.spotlight.source_point)
self.add(angle_arc)
# angle msmt (decimal number)
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: spotlight.opening_angle()/TAU * 360
ca3 = ContinualChangingDecimal(angle_indicator,angle_update_func)
self.add(ca3)
angle_update_func = lambda x: self.spotlight.opening_angle()/TAU * 360
ca1 = ContinualChangingDecimal(angle_indicator,angle_update_func)
self.add(ca1)
ca4 = AngleUpdater(angle_arc, spotlight)
self.add(ca4)
ca2 = AngleUpdater(angle_arc, self.spotlight)
self.add(ca2)
rotating_screen = ApplyMethod(spotlight.screen.rotate,
TAU/8, run_time=1.5)
#self.wait(2)
rotating_screen_2 = ApplyMethod(spotlight.screen.rotate,
-TAU/4, run_time=3, rate_func = there_and_back)
#self.wait(2)
rotating_screen_3 = ApplyMethod(spotlight.screen.rotate,
TAU/8, run_time=1.5)
self.play(rotating_screen)
def rotate_screen(self):
# rotating_screen_1 = Rotate(self.screen,
# TAU/8, run_time=1.5)
# #self.wait(2)
# rotating_screen_2 = Rotate(self.screen,
# -TAU/4, run_time=3)
# #self.wait(2)
# rotating_screen_3 = Rotate(self.screen,
# TAU/8, run_time=1.5)
# self.play(rotating_screen_1)
# self.play(rotating_screen_2)
# self.play(rotating_screen_3)
rotating_screen_1 = Rotate(self.screen, TAU/8, rate_func = there_and_back)
rotating_screen_2 = Rotate(self.screen, -TAU/8, rate_func = there_and_back)
self.play(rotating_screen_1)
self.play(rotating_screen_2)
self.play(rotating_screen_3)
#self.wait()
@ -880,8 +970,8 @@ class SingleLightHouseScene(PiCreatureScene):
# # morph into Earth scene
# globe = Circle(radius = 3)
# globe.move_to([2,0,0])
# earth = Circle(radius = 3)
# earth.move_to([2,0,0])
# sun_position = [-100,0,0]
# #self.add(screen_tracker)
# print "tuet"
@ -900,248 +990,382 @@ class SingleLightHouseScene(PiCreatureScene):
class EarthScene(Scene):
def construct(self):
DEGREES = TAU/360
radius = 2.5
center_x = 3
theta0 = 80 * DEGREES
theta0 = 70 * DEGREES
dtheta = 10 * DEGREES
theta1 = theta0 + dtheta
# screen
screen = Line([center_x - radius * np.cos(theta0),radius * np.sin(theta0),0],
[center_x - radius * np.cos(theta1),radius * np.sin(theta1),0])
screen.set_stroke(color = RED, width = 5)
globe = Circle(radius = radius, stroke_width = 0)
globe.move_to([center_x,0,0])
foreground_globe = globe.copy() # above the shadow
foreground_globe.radius -= 0.2
foreground_globe.set_stroke(color = WHITE, width = 1)
self.add_foreground_mobject(foreground_globe)
globe.add(screen)
# Earth
earth = Circle(radius = radius, stroke_width = 0)
earth.move_to([center_x,0,0])
foreground_earth = earth.copy() # above the shadow
foreground_earth.radius -= 0.2
foreground_earth.set_stroke(color = WHITE, width = 1)
self.add_foreground_mobject(foreground_earth)
earth.add(screen)
# Morty
morty = Mortimer().scale(0.3).next_to(screen, RIGHT, buff = 0.5)
self.add_foreground_mobject(morty)
# Light source (far-away Sun)
sun = Spotlight(
opacity_function = lambda r : 0.5,
num_levels = NUM_LEVELS,
radius = 100,
radius = 1100,
brightness = 5,
screen = screen
)
sun.move_source_to([-90,0,0])
self.add(globe,sun,screen)
sun.move_source_to([-1000,0,0])
# Add elements to scene
self.add(earth,sun,screen)
screen_tracker = ScreenTracker(sun)
self.add(screen_tracker)
# move screen to equator
self.play(
ApplyMethod(globe.rotate,theta0 + dtheta/2),
ApplyMethod(morty.move_to,[1.5,0,0])
Rotate(earth,theta0 + dtheta/2,run_time = 3),
ApplyMethod(morty.move_to,[1.5,0,0], run_time = 3),
)
class ScreenShapingScene(Scene):
def construct(self):
DEGREES = TAU / 360
self.setup_elements()
self.deform_screen()
self.create_brightness_rect()
self.slant_screen()
self.unslant_screen()
self.left_shift_screen_while_showing_light_indicator()
self.add_distance_arrow()
self.right_shift_screen_while_showing_light_indicator_and_distance_arrow()
self.left_shift_again()
self.morph_into_faux_3d()
screen_height = 1.0
brightness_rect_height = 1.0
screen = Line([3,-screen_height/2,0],[3,screen_height/2,0], path_arc = 0, num_arc_anchors = 10)
def setup_elements(self):
source = Spotlight(
self.screen_height = 1.0
self.brightness_rect_height = 1.0
# screen
self.screen = Line([3,-self.screen_height/2,0],[3,self.screen_height/2,0],
path_arc = 0, num_arc_anchors = 10)
# spotlight
self.spotlight = Spotlight(
opacity_function = inverse_quadratic(1,5,1),
num_levels = NUM_LEVELS,
radius = 10,
brightness = 5,
screen = screen
screen = self.screen
)
source.move_source_to([-5,0,0])
self.spotlight.move_source_to([-5,0,0])
screen_tracker = ScreenTracker(self.spotlight)
# lighthouse
lighthouse = LightHouse()
lighthouse.scale(2).next_to(self.spotlight.source_point,DOWN,buff=0)
# ambient light
ambient_light = AmbientLight(
opacity_function = inverse_quadratic(AMBIENT_DIMMED,1,1),
num_levels = NUM_LEVELS,
radius = 10,
brightness = 1,
)
lighthouse.scale(2).next_to(source.source_point,DOWN,buff=0)
ambient_light.move_source_to(source.source_point)
ambient_light.move_source_to(self.spotlight.source_point)
self.add(lighthouse, ambient_light,source,screen)
morty = Mortimer().scale(0.3).next_to(screen, RIGHT, buff = 0.5)
self.add_foreground_mobject(morty)
self.wait()
screen_tracker = ScreenTracker(source)
# Morty
self.morty = Mortimer().scale(0.3).next_to(self.screen, RIGHT, buff = 0.5)
# Add everything to the scene
self.add(lighthouse, ambient_light,self.spotlight,self.screen)
self.add_foreground_mobject(self.morty)
self.add(screen_tracker)
self.play(
ApplyMethod(screen.set_path_arc, 45 * DEGREES),
)
def deform_screen(self):
self.wait()
self.play(ApplyMethod(self.screen.set_path_arc, 45 * DEGREES))
self.play(ApplyMethod(self.screen.set_path_arc, -90 * DEGREES))
self.play(ApplyMethod(self.screen.set_path_arc, 0))
def create_brightness_rect(self):
# in preparation for the slanting, create a rectangle that shows the brightness
# a rect a zero width overlaying the screen
# so we can morph it into the brightness rect above
brightness_rect0 = Rectangle(width = 0,
height = self.screen_height).move_to(self.screen.get_center())
self.add_foreground_mobject(brightness_rect0)
self.brightness_rect = Rectangle(width = self.brightness_rect_height,
height = self.brightness_rect_height, fill_color = YELLOW, fill_opacity = 0.5)
self.brightness_rect.next_to(self.screen, UP, buff = 1)
self.play(
ApplyMethod(screen.set_path_arc, -90 * DEGREES),
ReplacementTransform(brightness_rect0,self.brightness_rect)
)
self.play(
ApplyMethod(screen.set_path_arc, 0),
)
# in preparation for the slanting, create a rectangle that show the brightness
rect_origin = Rectangle(width = 0, height = screen_height).move_to(screen.get_center())
self.add_foreground_mobject(rect_origin)
brightness_rect = Rectangle(width = brightness_rect_height,
height = brightness_rect_height, fill_color = YELLOW, fill_opacity = 0.5)
brightness_rect.next_to(screen, UP, buff = 1)
self.play(
ReplacementTransform(rect_origin,brightness_rect)
)
original_screen = screen.copy()
original_brightness_rect = brightness_rect.copy()
self.original_screen = self.screen.copy()
self.original_brightness_rect = self.brightness_rect.copy()
# for unslanting the screen later
lower_screen_point, upper_screen_point = screen.get_start_and_end()
def slant_screen(self):
lower_screen_point, upper_screen_point = self.screen.get_start_and_end()
lower_slanted_screen_point = interpolate(
lower_screen_point, source.source_point, 0.2
lower_screen_point, self.spotlight.source_point, 0.2
)
upper_slanted_screen_point = interpolate(
upper_screen_point, source.source_point, -0.2
upper_screen_point, self.spotlight.source_point, -0.2
)
slanted_brightness_rect = brightness_rect.copy()
slanted_brightness_rect.width *= 2
slanted_brightness_rect.generate_points()
slanted_brightness_rect.set_fill(opacity = 0.25)
self.slanted_brightness_rect = self.brightness_rect.copy()
self.slanted_brightness_rect.width *= 2
self.slanted_brightness_rect.generate_points()
self.slanted_brightness_rect.set_fill(opacity = 0.25)
slanted_screen = Line(lower_slanted_screen_point,upper_slanted_screen_point,
self.slanted_screen = Line(lower_slanted_screen_point,upper_slanted_screen_point,
path_arc = 0, num_arc_anchors = 10)
slanted_brightness_rect.move_to(brightness_rect.get_center())
self.slanted_brightness_rect.move_to(self.brightness_rect.get_center())
self.play(
ReplacementTransform(screen,slanted_screen),
ReplacementTransform(brightness_rect,slanted_brightness_rect),
ReplacementTransform(self.screen,self.slanted_screen),
ReplacementTransform(self.brightness_rect,self.slanted_brightness_rect),
)
def unslant_screen(self):
self.wait()
screen = slanted_screen
source.screen = screen
self.remove(slanted_screen)
brightness_rect = slanted_brightness_rect
self.remove(slanted_brightness_rect)
self.remove(self.slanted_screen)
self.remove(self.slanted_brightness_rect)
self.play(
ReplacementTransform(screen,original_screen),
ReplacementTransform(brightness_rect,original_brightness_rect),
ReplacementTransform(self.screen,self.original_screen),
ReplacementTransform(self.slanted_brightness_rect,self.original_brightness_rect),
)
self.remove(original_brightness_rect)
#self.remove(self.original_brightness_rect)
def left_shift_screen_while_showing_light_indicator(self):
# Scene 5: constant screen size, changing opening angle
# let's use an actual light indicator instead of just rects
indicator_intensity = 0.25
indicator_height = 1.25 * screen_height
self.indicator_intensity = 0.25
indicator_height = 1.25 * self.screen_height
# indicator_origin = Ellipse(width = 0, height = screen_height).move_to(screen.get_center())
# indicator_origin.set_fill(opacity = 0)
# self.add_foreground_mobject(indicator_origin)
# indicator0 = Ellipse(width = 0, height = screen_height).move_to(self.screen.get_center())
# indicator0.set_fill(opacity = 0)
# self.add_foreground_mobject(indicator0)
indicator = LightIndicator(radius = indicator_height/2,
self.indicator = LightIndicator(radius = indicator_height/2,
opacity_for_unit_intensity = OPACITY_FOR_UNIT_INTENSITY,
color = LIGHT_COLOR,
precision = 2)
indicator.set_intensity(indicator_intensity)
self.indicator.set_intensity(self.indicator_intensity)
indicator.move_to(slanted_brightness_rect.get_center())
self.indicator.move_to(self.original_brightness_rect.get_center())
self.play(
FadeOut(slanted_brightness_rect),
FadeIn(indicator)
FadeOut(self.original_brightness_rect),
FadeIn(self.indicator)
)
self.add_foreground_mobject(indicator.reading)
self.add_foreground_mobject(self.indicator.reading)
new_indicator_intensity = 1.0
self.unit_indicator_intensity = 1.0
# shifted_brightness_rect = brightness_rect.copy()
# shifted_brightness_rect.shift([-3,0,0]).set_fill(opacity = 0.8)
left_shift = (screen.get_center()[0] - source.source_point[0])/2
self.left_shift = (self.screen.get_center()[0] - self.spotlight.source_point[0])/2
self.play(
ApplyMethod(screen.shift,[-left_shift,0,0]),
ApplyMethod(morty.shift,[-left_shift,0,0]),
ApplyMethod(indicator.shift,[-left_shift,0,0]),
ApplyMethod(indicator.set_intensity,new_indicator_intensity),
ApplyMethod(self.screen.shift,[-self.left_shift,0,0]),
ApplyMethod(self.morty.shift,[-self.left_shift,0,0]),
#ApplyMethod(self.indicator.shift,[-self.left_shift,0,0]),
ApplyMethod(self.indicator.set_intensity,self.unit_indicator_intensity),
)
self.remove(original_screen) # was still hiding behind the shadow
self.remove(self.original_screen) # was still hiding behind the shadow
# add distance indicator
left_x = source.source_point[0]
right_x = screen.get_center()[0]
line_y = -2
line1 = Arrow([left_x,line_y,0],[right_x,line_y,0])
line2 = Arrow([right_x,line_y,0],[left_x,line_y,0])
line1.set_fill(color = WHITE)
line2.set_fill(color = WHITE)
distance_decimal = Integer(1).next_to(line1,DOWN)
line = VGroup(line1, line2,distance_decimal)
self.add(line)
# move everything away
distance_to_source = right_x - left_x
new_right_x = left_x + 2*distance_to_source
new_line1 = Arrow([left_x,line_y,0],[new_right_x,line_y,0])
new_line2 = Arrow([new_right_x,line_y,0],[left_x,line_y,0])
new_line1.set_fill(color = WHITE)
new_line2.set_fill(color = WHITE)
new_distance_decimal = Integer(2).next_to(new_line1,DOWN)
new_line = VGroup(new_line1, new_line2, new_distance_decimal)
def add_distance_arrow(self):
# distance arrow (length 1)
left_x = self.spotlight.source_point[0]
right_x = self.screen.get_center()[0]
arrow_y = -2
arrow1 = Arrow([left_x,arrow_y,0],[right_x,arrow_y,0])
arrow2 = Arrow([right_x,arrow_y,0],[left_x,arrow_y,0])
arrow1.set_fill(color = WHITE)
arrow2.set_fill(color = WHITE)
distance_decimal = Integer(1).next_to(arrow1,DOWN)
self.arrow = VGroup(arrow1, arrow2,distance_decimal)
self.add(self.arrow)
# distance arrow (length 2)
# will be morphed into
self.distance_to_source = right_x - left_x
new_right_x = left_x + 2 * self.distance_to_source
new_arrow1 = Arrow([left_x,arrow_y,0],[new_right_x,arrow_y,0])
new_arrow2 = Arrow([new_right_x,arrow_y,0],[left_x,arrow_y,0])
new_arrow1.set_fill(color = WHITE)
new_arrow2.set_fill(color = WHITE)
new_distance_decimal = Integer(2).next_to(new_arrow1,DOWN)
self.new_arrow = VGroup(new_arrow1, new_arrow2, new_distance_decimal)
# don't add it yet
def right_shift_screen_while_showing_light_indicator_and_distance_arrow(self):
self.wait()
self.play(
ReplacementTransform(line,new_line),
ApplyMethod(screen.shift,[distance_to_source,0,0]),
ApplyMethod(indicator.shift,[left_shift,0,0]),
ApplyMethod(indicator.set_intensity,indicator_intensity),
ApplyMethod(morty.shift,[distance_to_source,0,0]),
ReplacementTransform(self.arrow,self.new_arrow),
ApplyMethod(self.screen.shift,[self.distance_to_source,0,0]),
#ApplyMethod(self.indicator.shift,[self.left_shift,0,0]),
ApplyMethod(self.indicator.set_intensity,self.indicator_intensity),
# this should trigger ChangingDecimal, but it doesn't
# maybe bc it's an anim within an anim?
ApplyMethod(self.morty.shift,[self.distance_to_source,0,0]),
)
def left_shift_again(self):
self.wait()
self.play(
ReplacementTransform(self.new_arrow,self.arrow),
ApplyMethod(self.screen.shift,[-self.distance_to_source,0,0]),
#ApplyMethod(self.indicator.shift,[-self.left_shift,0,0]),
ApplyMethod(self.indicator.set_intensity,self.unit_indicator_intensity),
ApplyMethod(self.morty.shift,[-self.distance_to_source,0,0]),
)
def morph_into_faux_3d(self):
p0_lower, p0_upper = self.screen.get_start_and_end()
p01 = p0_lower
p02 = p0_lower
p03 = p0_upper
p04 = p0_upper
screen3d0 = Polygon(p01,p02,p03,p04)
screen3d0.set_stroke(width = 1, color = WHITE)
screen3d0_edge = VMobject()
screen3d0_edge.set_anchor_points([p03,p04,p01])
perspective_v = 0.5 * np.array([1,-1,0])
p1 = p01 + 0.5 * perspective_v
p2 = p02 - 0.5 * perspective_v
p3 = p03 - 0.5 * perspective_v
p4 = p04 + 0.5 * perspective_v
screen3d = Polygon(p1,p2,p3,p4)
screen3d.set_fill(color = WHITE, opacity = 0.5)
screen3d_edge = VMobject()
screen3d_edge.set_anchor_points([p3,p4,p1])
self.spotlight.screen = screen3d0_edge
self.play(Transform(self.spotlight.screen,screen3d_edge))