mirror of
https://github.com/3b1b/manim.git
synced 2025-08-05 16:49:03 +00:00
Added working light, scenes 3 and 4
This commit is contained in:
parent
526929098a
commit
68c08f0a5a
1 changed files with 410 additions and 233 deletions
|
@ -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):
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue