Added working light, scenes 3 and 4

This commit is contained in:
Ben Hambrecht 2018-01-27 13:30:25 +01:00
parent 526929098a
commit 68c08f0a5a

View file

@ -30,9 +30,9 @@ from mobject.vectorized_mobject import *
## To watch one of these scenes, run the following:
## python extract_scene.py -p file_name <SceneName>
inverse_power_law = lambda maxint,cutoff,exponent: \
(lambda r: maxint * (cutoff/(r+cutoff))**exponent)
inverse_quadratic = lambda maxint,cutoff: inverse_power_law(maxint,cutoff,2)
inverse_power_law = lambda maxint,scale,cutoff,exponent: \
(lambda r: maxint * (cutoff/(r/scale+cutoff))**exponent)
inverse_quadratic = lambda maxint,scale,cutoff: inverse_power_law(maxint,scale,cutoff,2)
LIGHT_COLOR = YELLOW
INDICATOR_RADIUS = 0.7
@ -44,12 +44,13 @@ FAST_INDICATOR_UPDATE_TIME = 0.1
OPACITY_FOR_UNIT_INTENSITY = 0.2
SWITCH_ON_RUN_TIME = 2.5
FAST_SWITCH_ON_RUN_TIME = 0.1
LIGHT_CONE_NUM_SECTORS = 30
NUM_LEVELS = 30
NUM_CONES = 50 # in first lighthouse scene
NUM_VISIBLE_CONES = 5 # ibidem
ARC_TIP_LENGTH = 0.2
DIM_OPACITY = 0.2
DEGREES = TAU/360
def show_line_length(line):
v = line.points[1] - line.points[0]
@ -67,7 +68,7 @@ class AngleUpdater(ContinualAnimation):
def update_mobject(self, dt):
# angle arc
new_arc = self.angle_arc.copy().set_bound_angles(
start = self.lc.start_angle,
start = self.lc.start_angle(),
stop = self.lc.stop_angle()
)
new_arc.generate_points()
@ -77,55 +78,167 @@ class AngleUpdater(ContinualAnimation):
LIGHT_COLOR = YELLOW
DEGREES = 360/TAU
SWITCH_ON_RUN_TIME = 1.5
class LightScreen(VMobject):
# A light screen is composed of a VMobject and a light cone.
# It has knowledge of the light source point.
# As the screen changes, it calculates the viewing angle from
# the source and updates the light cone.
def __init__(self, light_source = ORIGIN, screen = None, light_cone = None):
Mobject.__init__(self)
self.light_cone = light_cone
self.light_source = light_source
self.screen = screen
self.light_cone.move_source_to(self.light_source)
self.shadow = VMobject(fill_color = BLACK, stroke_width = 0, fill_opacity = 1.0)
self.add(self.light_cone, self.screen, self.shadow)
self.update_shadow(self.shadow)
class AmbientLight(VMobject):
# Parameters are:
# * a source point
# * an opacity function
# * a light color
# * a max opacity
# * a radius (larger than the opacity's dropoff length)
# * the number of subdivisions (levels, annuli)
CONFIG = {
"source_point" : ORIGIN,
"opacity_function" : lambda r : 1.0/(r+1.0)**2,
"color" : LIGHT_COLOR,
"max_opacity" : 1.0,
"num_levels" : 10,
"radius" : 5.0
}
def generate_points(self):
# in theory, this method is only called once, right?
# so removing submobs shd not be necessary
for submob in self.submobjects:
self.remove(submob)
# create annuli
self.radius = float(self.radius)
dr = self.radius / self.num_levels
for r in np.arange(0, self.radius, dr):
alpha = self.max_opacity * self.opacity_function(r)
annulus = Annulus(
inner_radius = r,
outer_radius = r + dr,
color = self.color,
fill_opacity = alpha
)
annulus.move_arc_center_to(self.source_point)
self.add(annulus)
def move_source_to(self,point):
self.shift(point - self.source_point)
self.source_point = np.array(point)
# for submob in self.submobjects:
# if type(submob) == Annulus:
# submob.shift(self.source_point - submob.get_center())
def dimming(self,new_alpha):
old_alpha = self.max_opacity
self.max_opacity = new_alpha
for submob in self.submobjects:
old_submob_alpha = submob.fill_opacity
new_submob_alpha = old_submob_alpha * new_alpha/old_alpha
submob.set_fill(opacity = new_submob_alpha)
class Spotlight(VMobject):
CONFIG = {
"source_point" : ORIGIN,
"opacity_function" : lambda r : 1.0/(r/2+1.0)**2,
"color" : LIGHT_COLOR,
"max_opacity" : 1.0,
"num_levels" : 10,
"radius" : 5.0,
"screen" : None,
"shadow" : VMobject(fill_color = BLACK, stroke_width = 0, fill_opacity = 1.0)
}
def track_screen(self):
self.generate_points()
def generate_points(self):
for submob in self.submobjects:
self.remove(submob)
if self.screen != None:
# look for the screen and create annular sectors
lower_angle, upper_angle = self.viewing_angles(self.screen)
self.radius = float(self.radius)
dr = self.radius / self.num_levels
for r in np.arange(0, self.radius, dr):
alpha = self.max_opacity * self.opacity_function(r)
annular_sector = AnnularSector(
inner_radius = r,
outer_radius = r + dr,
color = self.color,
fill_opacity = alpha,
start_angle = lower_angle,
angle = upper_angle - lower_angle
)
annular_sector.move_arc_center_to(self.source_point)
self.add(annular_sector)
self.update_shadow(point = self.source_point)
self.add(self.shadow)
def update_light_cone(self,lc):
lower_angle, upper_angle = self.viewing_angles()
#print lower_angle, upper_angle
self.light_cone.update_opening(start_angle = lower_angle,
stop_angle = upper_angle)
return self
def viewing_angle_of_point(self,point):
distance_vector = point - self.light_source
distance_vector = point - self.source_point
angle = angle_of_vector(distance_vector)
return angle
def viewing_angles(self):
all_points = []
def viewing_angles(self,screen):
for submob in self.family_members_with_points():
all_points.extend(submob.get_anchors())
viewing_angles = np.array(map(self.viewing_angle_of_point, self.screen.get_anchors()))
if len(viewing_angles) == 0:
lower_angle = upper_angle = 0
else:
viewing_angles = np.array(map(self.viewing_angle_of_point,
screen.get_anchors()))
lower_angle = upper_angle = 0
if len(viewing_angles) != 0:
lower_angle = np.min(viewing_angles)
upper_angle = np.max(viewing_angles)
return lower_angle, upper_angle
def update_shadow(self,sh):
def opening_angle(self):
l,u = self.viewing_angles(self.screen)
return u - l
def start_angle(self):
l,u = self.viewing_angles(self.screen)
return l
def stop_angle(self):
l,u = self.viewing_angles(self.screen)
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)
def recalculate_sectors(self, point = ORIGIN, screen = None):
if screen == None:
return
for submob in self.submobject_family():
if type(submob) == AnnularSector:
lower_angle, upper_angle = self.viewing_angles(screen)
new_submob = AnnularSector(
start_angle = lower_angle,
angle = upper_angle - lower_angle,
inner_radius = submob.inner_radius,
outer_radius = submob.outer_radius
)
new_submob.move_arc_center_to(point)
submob.points = new_submob.points
def update_shadow(self,point = ORIGIN):
use_point = point #self.source_point
self.shadow.points = self.screen.points
ray1 = self.screen.points[0] - self.light_source
ray2 = self.screen.points[-1] - self.light_source
ray1 = self.screen.points[0] - use_point
ray2 = self.screen.points[-1] - use_point
ray1 = ray1/np.linalg.norm(ray1) * 100
ray1 = rotate_vector(ray1,-TAU/16)
ray2 = ray2/np.linalg.norm(ray2) * 100
@ -135,121 +248,36 @@ class LightScreen(VMobject):
self.shadow.add_control_points([outpoint2,outpoint1,self.screen.points[0]])
self.shadow.mark_paths_closed = True
def move_source_to(self,new_point):
self.light_source = new_point
#self.update_light_cone(self.light_cone)
def dimming(self,new_alpha):
old_alpha = self.max_opacity
self.max_opacity = new_alpha
for submob in self.submobjects:
if type(submob) != AnnularSector:
# it's the shadow, don't dim it
continue
old_submob_alpha = submob.fill_opacity
new_submob_alpha = old_submob_alpha * new_alpha/old_alpha
submob.set_fill(opacity = new_submob_alpha)
def change_opacity_function(self,new_f):
self.radius = 120
self.opacity_function = new_f
dr = self.radius/self.num_levels
class LightCone(VGroup):
CONFIG = {
"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,
}
def generate_points(self):
radii = np.linspace(0, self.radius, self.num_sectors+1)
sectors = [
AnnularSector(
start_angle = self.start_angle,
angle = self.angle,
inner_radius = r1,
outer_radius = r2,
stroke_width = 0,
stroke_color = self.color,
fill_color = self.color,
fill_opacity = self.brightness * self.opacity_function(r1),
)
for r1, r2 in zip(radii, radii[1:])
]
self.add(*sectors)
def get_source_point(self):
if len(self.submobjects) == 0:
return None
source = self.submobjects[0].get_arc_center()
return source
def move_source_to(self,point):
if len(self.submobjects) == 0:
return
source = self.submobjects[0].get_arc_center()
self.shift(point - source)
self.generate_points()
def update_opening(self, start_angle, stop_angle):
self.start_angle = start_angle
self.angle = stop_angle - start_angle
source_point = self.get_source_point()
sectors = []
for submob in self.submobjects:
if type(submob) == AnnularSector:
sectors.append(submob)
submob.start_angle = self.start_angle
submob.angle = self.angle
submob.generate_points()
submob.shift(source_point - submob.get_arc_center())
print self.num_levels, len(sectors)
for (r,submob) in zip(np.arange(0,self.radius,dr),sectors):
if type(submob) != AnnularSector:
# it's the shadow, don't dim it
continue
alpha = self.opacity_function(r)
submob.set_fill(opacity = alpha)
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
class Candle(VGroup):
CONFIG = {
"radius" : 5,
"brightness" : 1.0,
"opacity_function" : lambda r : 1./max(r, 0.01),
"num_annuli" : 10,
"color": LIGHT_COLOR,
}
def generate_points(self):
radii = np.linspace(0, self.radius, self.num_annuli+1)
annuli = [
Annulus(
inner_radius = r1,
outer_radius = r2,
stroke_width = 0,
stroke_color = self.color,
fill_color = self.color,
fill_opacity = self.brightness * self.opacity_function(r1),
)
for r1, r2 in zip(radii, radii[1:])
]
self.add(*annuli)
def get_source_point(self):
if len(self.submobjects) == 0:
return None
source = self.submobjects[0].get_center()
return source
def move_source_to(self,point):
if len(self.submobjects) == 0:
return
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))
@ -261,11 +289,42 @@ class SwitchOn(LaggedStart):
}
def __init__(self, light, **kwargs):
if not isinstance(light,LightCone) and not isinstance(light,Candle):
if not isinstance(light,AmbientLight) and not isinstance(light,Spotlight):
raise Exception("Only LightCones and Candles can be switched on")
LaggedStart.__init__(self,
FadeIn, light, **kwargs)
class SwitchOff(LaggedStart):
CONFIG = {
"lag_ratio": 0.2,
"run_time": SWITCH_ON_RUN_TIME
}
def __init__(self, light, **kwargs):
if not isinstance(light,AmbientLight) and not isinstance(light,Spotlight):
raise Exception("Only LightCones and Candles can be switched on")
light.submobjects = light.submobjects[::-1]
LaggedStart.__init__(self,
FadeOut, light, **kwargs)
light.submobjects = light.submobjects[::-1]
class ScreenTracker(ContinualAnimation):
def __init__(self, mobject, **kwargs):
ContinualAnimation.__init__(self, mobject, **kwargs)
def update_mobject(self, dt):
self.mobject.recalculate_sectors(
point = self.mobject.source_point,
screen = self.mobject.screen)
self.mobject.update_shadow(self.mobject.source_point)
class LightHouse(SVGMobject):
CONFIG = {
"file_name" : "lighthouse",
@ -606,7 +665,7 @@ class FirstLightHouseScene(PiCreatureScene):
lighthouses = []
lighthouse_pos = []
light_cones = []
ambient_lights = []
euler_sum_above = TexMobject("1", "+", "{1\over 4}",
@ -625,15 +684,15 @@ class FirstLightHouseScene(PiCreatureScene):
for i in range(1,NUM_CONES+1):
lighthouse = LightHouse()
point = self.number_line.number_to_point(i)
light_cone = Candle(
opacity_function = inverse_quadratic(1,1),
num_annuli = LIGHT_CONE_NUM_SECTORS,
radius = 12)
ambient_light = AmbientLight(
opacity_function = inverse_quadratic(1,2,1),
num_levels = NUM_LEVELS,
radius = 12.0)
light_cone.move_source_to(point)
ambient_light.move_source_to(point)
lighthouse.next_to(point,DOWN,0)
lighthouses.append(lighthouse)
light_cones.append(light_cone)
ambient_lights.append(ambient_light)
lighthouse_pos.append(point)
@ -649,13 +708,13 @@ 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]):
indicator_start_time = 0.4 * (i+1) * SWITCH_ON_RUN_TIME/lc.radius * self.number_line.unit_size
for (i,ambient_light) in zip(range(NUM_VISIBLE_CONES),ambient_lights[:NUM_VISIBLE_CONES]):
indicator_start_time = 0.4 * (i+1) * SWITCH_ON_RUN_TIME/ambient_light.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)
self.play(
SwitchOn(lc),
SwitchOn(ambient_light),
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,
@ -675,13 +734,13 @@ class FirstLightHouseScene(PiCreatureScene):
)
# 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]):
indicator_start_time = 0.5 * (i+1) * FAST_SWITCH_ON_RUN_TIME/lc.radius * self.number_line.unit_size
for (i,ambient_light) in zip(range(NUM_VISIBLE_CONES,NUM_CONES),ambient_lights[NUM_VISIBLE_CONES:NUM_CONES]):
indicator_start_time = 0.5 * (i+1) * FAST_SWITCH_ON_RUN_TIME/ambient_light.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),
SwitchOn(ambient_light, 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])
@ -726,72 +785,58 @@ class SingleLightHouseScene(PiCreatureScene):
observer_point = [DISTANCE_FROM_LIGHTHOUSE/2,0,0]
lighthouse = LightHouse()
candle = Candle(
opacity_function = inverse_quadratic(1,1),
num_annuli = LIGHT_CONE_NUM_SECTORS,
ambient_light = AmbientLight(
opacity_function = inverse_quadratic(1,2,1),
num_levels = NUM_LEVELS,
radius = 10,
brightness = 1,
)
lighthouse.scale(2).next_to(source_point, DOWN, buff = 0)
candle.move_to(source_point)
ambient_light.move_to(source_point)
morty = self.get_primary_pi_creature()
morty.scale(0.5)
morty.move_to(observer_point)
self.add(lighthouse)
self.play(
SwitchOn(candle)
SwitchOn(ambient_light)
)
light_cone = LightCone(
opacity_function = inverse_quadratic(1,1),
num_sectors = LIGHT_CONE_NUM_SECTORS,
screen = Line([0,-1,0],[0,1,0])
screen.rotate(-TAU/6)
screen.next_to(morty, LEFT, buff = 1)
spotlight = Spotlight(
opacity_function = inverse_quadratic(1,2,1),
num_levels = NUM_LEVELS,
radius = 10,
brightness = 5,
screen = screen
)
light_cone.move_source_to(source_point)
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.play(
# 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),
# )
spotlight.move_source_to(source_point)
lc_updater = lambda lc: light_screen.update_light_cone(lc)
sh_updater = lambda sh: light_screen.update_shadow(sh)
self.play(
ApplyMethod(ambient_light.dimming,0.2),
FadeIn(spotlight))
self.add(spotlight.shadow)
ca1 = ContinualUpdateFromFunc(light_screen.light_cone,
lc_updater)
ca15 = ContinualUpdateFromFunc(light_screen,
lc_updater)
ca2 = ContinualUpdateFromFunc(light_screen.shadow,
sh_updater)
self.add(ca1, ca15, ca2)
self.add_foreground_mobject(morty)
pointing_screen_at_source = ApplyMethod(screen.rotate,TAU/6)
#self.play(pointing_screen_at_source)
#self.wait()
screen_tracker = ScreenTracker(spotlight)
# activate ONLY when spotlight is moving!
arc_angle = light_cone.angle
self.add(screen_tracker)
pointing_screen_at_source = ApplyMethod(spotlight.screen.rotate,TAU/6)
self.play(pointing_screen_at_source)
arc_angle = spotlight.opening_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 = Arc(radius = 5, start_angle = spotlight.start_angle(),
angle = 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)
@ -803,31 +848,28 @@ class SingleLightHouseScene(PiCreatureScene):
angle_indicator.next_to(angle_arc,RIGHT)
self.add_foreground_mobject(angle_indicator)
angle_update_func = lambda x: light_cone.angle/TAU * 360
angle_update_func = lambda x: spotlight.opening_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)
ca4 = AngleUpdater(angle_arc, spotlight)
self.add(ca4)
rotating_screen = ApplyMethod(light_screen.screen.rotate,
rotating_screen = ApplyMethod(spotlight.screen.rotate,
TAU/8, run_time=1.5)
#self.wait(2)
rotating_screen_2 = ApplyMethod(light_screen.screen.rotate,
rotating_screen_2 = ApplyMethod(spotlight.screen.rotate,
-TAU/4, run_time=3, rate_func = there_and_back)
#self.wait(2)
rotating_screen_3 = ApplyMethod(light_screen.screen.rotate,
rotating_screen_3 = ApplyMethod(spotlight.screen.rotate,
TAU/8, run_time=1.5)
#self.play(rotating_screen)
#self.play(rotating_screen_2)
#self.play(rotating_screen_3)
self.play(rotating_screen)
self.play(rotating_screen_2)
self.play(rotating_screen_3)
#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()
#self.wait()
@ -836,10 +878,14 @@ class SingleLightHouseScene(PiCreatureScene):
globe = Circle(radius = 3)
globe.move_to([2,0,0])
sun_position = [-100,0,0]
#self.add(screen_tracker)
print "tuet"
self.remove(screen_tracker)
new_opacity_function = lambda r: 0.5
self.play(
#ApplyMethod(lighthouse.move_to,sun_position),
#ApplyMethod(candle.move_to,sun_position),
ApplyMethod(light_screen.move_source_to,sun_position),
ApplyMethod(lighthouse.move_to,sun_position),
ApplyMethod(ambient_light.move_to,sun_position),
ApplyMethod(spotlight.move_source_to,sun_position),
#FadeOut(angle_arc),
#FadeOut(angle_indicator),
#FadeIn(globe),
@ -847,6 +893,148 @@ class SingleLightHouseScene(PiCreatureScene):
#ApplyMethod(morty.move_to,[1,0,0])
)
self.play(
ApplyMethod(spotlight.change_opacity_function,new_opacity_function))
self.add(screen_tracker)
class EarthScene(Scene):
def construct(self):
DEGREES = TAU/360
radius = 2.5
center_x = 3
theta0 = 80 * DEGREES
dtheta = 10 * DEGREES
theta1 = theta0 + dtheta
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)
morty = Mortimer().scale(0.3).next_to(screen, RIGHT, buff = 0.5)
self.add_foreground_mobject(morty)
sun = Spotlight(
opacity_function = lambda r : 0.5,
num_levels = NUM_LEVELS,
radius = 100,
brightness = 5,
screen = screen
)
sun.move_source_to([-90,0,0])
self.add(globe,sun,screen)
screen_tracker = ScreenTracker(sun)
self.add(screen_tracker)
self.play(
ApplyMethod(globe.rotate,theta0 + dtheta/2),
ApplyMethod(morty.move_to,[1.5,0,0])
)
class ScreenShapingScene(Scene):
def construct(self):
DEGREES = TAU / 360
screen = Line([2,-1,0],[2,1,0], path_arc = 0, num_arc_anchors = 10)
source = Spotlight(
opacity_function = inverse_quadratic(1,5,1),
num_levels = NUM_LEVELS,
radius = 10,
brightness = 5,
screen = screen
)
source.move_source_to([-5,0,0])
lighthouse = LightHouse()
ambient_light = AmbientLight(
opacity_function = inverse_quadratic(1,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)
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)
self.add(screen_tracker)
self.play(
ApplyMethod(screen.set_path_arc, 45 * DEGREES),
)
self.play(
ApplyMethod(screen.set_path_arc, -90 * DEGREES),
)
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 = 2).move_to(screen.get_center())
self.add_foreground_mobject(rect_origin)
brightness_rect = Rectangle(width = 2, height = 2, fill_color = YELLOW, fill_opacity = 0.5)
brightness_rect.move_to([3,3,0])
self.play(
ReplacementTransform(rect_origin,brightness_rect)
)
lower_screen_point, upper_screen_point = screen.get_start_and_end()
lower_slanted_screen_point = interpolate(
lower_screen_point, source.source_point, 0.2
)
upper_slanted_screen_point = interpolate(
upper_screen_point, source.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)
slanted_screen = Line(lower_slanted_screen_point,upper_slanted_screen_point, path_arc = 0, num_arc_anchors = 10)
self.play(
ReplacementTransform(screen,slanted_screen),
#ApplyMethod(brightness_rect.stretch,2,0), # factor = 2, dim = 0 (x)
ReplacementTransform(brightness_rect,slanted_brightness_rect),
ApplyMethod(brightness_rect.set_fill,{"opacity" : 0.25}),
)
@ -858,17 +1046,6 @@ class SingleLightHouseScene(PiCreatureScene):