Merge branch 'lighthouse-grant' into lighthouse2

This commit is contained in:
Grant Sanderson 2018-03-05 20:11:45 -08:00
commit 3944c8a2d8
11 changed files with 4711 additions and 100 deletions

View file

@ -426,7 +426,6 @@ class IntroScene(PiCreatureScene):
self.ellipsis = TexMobject("\cdots") self.ellipsis = TexMobject("\cdots")
self.ellipsis.scale(0.4) self.ellipsis.scale(0.4)
for i in range(5, max_n1): for i in range(5, max_n1):
if i == 5: if i == 5:
@ -441,7 +440,6 @@ class IntroScene(PiCreatureScene):
GrowFromPoint(self.rects[i], self.euler_sum[10].get_center(), GrowFromPoint(self.rects[i], self.euler_sum[10].get_center(),
run_time = 0.5) run_time = 0.5)
) )
for i in range(max_n1, max_n2): for i in range(max_n1, max_n2):
self.play( self.play(
GrowFromPoint(self.rects[i], self.euler_sum[10].get_center(), GrowFromPoint(self.rects[i], self.euler_sum[10].get_center(),
@ -929,15 +927,11 @@ class SingleLighthouseScene(PiCreatureScene):
class MorphIntoSunScene(PiCreatureScene): class MorphIntoSunScene(PiCreatureScene):
def construct(self): def construct(self):
self.setup_elements() self.setup_elements()
self.morph_lighthouse_into_sun() self.morph_lighthouse_into_sun()
def setup_elements(self): def setup_elements(self):
self.remove(self.get_primary_pi_creature()) self.remove(self.get_primary_pi_creature())
SCREEN_SIZE = 3.0 SCREEN_SIZE = 3.0
@ -988,10 +982,8 @@ class MorphIntoSunScene(PiCreatureScene):
self.add_foreground_mobject(self.light_source.shadow) self.add_foreground_mobject(self.light_source.shadow)
self.add_foreground_mobject(morty) self.add_foreground_mobject(morty)
self.light_source.dim_ambient self.light_source.dim_ambient
self.add(self.light_source.spotlight) self.add(self.light_source.spotlight)
self.screen_tracker = ScreenTracker(self.light_source) self.screen_tracker = ScreenTracker(self.light_source)
self.add(self.screen_tracker) self.add(self.screen_tracker)
@ -4351,10 +4343,6 @@ class ArcHighlightOverlayScene(Scene):
) )
flash_arcs(3)
class ThumbnailScene(Scene): class ThumbnailScene(Scene):
@ -4545,14 +4533,4 @@ class RightAnglesOverlay(Scene):
self.play(FadeOut(lines)) self.play(FadeOut(lines))
flash_arcs(3)

4651
active_projects/basel2.py Normal file

File diff suppressed because it is too large Load diff

View file

@ -101,9 +101,15 @@ class Swap(CyclicReplace):
pass #Renaming, more understandable for two entries pass #Renaming, more understandable for two entries
class GrowFromPoint(Transform): class GrowFromPoint(Transform):
CONFIG = {
"point_color" : None,
}
def __init__(self, mobject, point, **kwargs): def __init__(self, mobject, point, **kwargs):
digest_config(self, kwargs)
target = mobject.copy() target = mobject.copy()
point_mob = Point(point) point_mob = Point(point)
if self.point_color:
point_mob.highlight(self.point_color)
mobject.replace(point_mob) mobject.replace(point_mob)
mobject.highlight(point_mob.get_color()) mobject.highlight(point_mob.get_color())
Transform.__init__(self, mobject, target, **kwargs) Transform.__init__(self, mobject, target, **kwargs)

View file

@ -18,7 +18,6 @@ from camera import Camera
HELP_MESSAGE = """ HELP_MESSAGE = """
Usage: Usage:
python extract_scene.py <module> [<scene name>] python extract_scene.py <module> [<scene name>]
-p preview in low quality -p preview in low quality
-s show and save picture of last frame -s show and save picture of last frame
-w write result to file [this is default if nothing else is stated] -w write result to file [this is default if nothing else is stated]
@ -35,7 +34,6 @@ SCENE_NOT_FOUND_MESSAGE = """
CHOOSE_NUMBER_MESSAGE = """ CHOOSE_NUMBER_MESSAGE = """
Choose number corresponding to desired scene/arguments. Choose number corresponding to desired scene/arguments.
(Use comma separated list for multiple entries) (Use comma separated list for multiple entries)
Choice(s): """ Choice(s): """
INVALID_NUMBER_MESSAGE = "Fine then, if you don't want to give a valid number I'll just quit" INVALID_NUMBER_MESSAGE = "Fine then, if you don't want to give a valid number I'll just quit"
@ -95,6 +93,7 @@ def get_configuration():
"save_pngs" : args.save_pngs, "save_pngs" : args.save_pngs,
#If -t is passed in (for transparent), this will be RGBA #If -t is passed in (for transparent), this will be RGBA
"saved_image_mode": "RGBA" if args.transparent else "RGB", "saved_image_mode": "RGBA" if args.transparent else "RGB",
"movie_file_extension" : ".mov" if args.transparent else ".mp4",
"quiet" : args.quiet or args.write_all, "quiet" : args.quiet or args.write_all,
"ignore_waits" : args.preview, "ignore_waits" : args.preview,
"write_all" : args.write_all, "write_all" : args.write_all,
@ -237,6 +236,7 @@ def main():
"write_to_movie", "write_to_movie",
"output_directory", "output_directory",
"save_pngs", "save_pngs",
"movie_file_extension",
"start_at_animation_number", "start_at_animation_number",
"end_at_animation_number", "end_at_animation_number",
] ]
@ -260,4 +260,4 @@ def main():
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View file

@ -693,6 +693,7 @@ class DictAsObject(object):
def __init__(self, dict): def __init__(self, dict):
self.__dict__ = dict self.__dict__ = dict
# Just to have a less heavyweight name for this extremely common operation
# #
# We may wish to have more fine-grained control over division by zero behavior # We may wish to have more fine-grained control over division by zero behavior
# in the future (separate specifiable values for 0/0 and x/0 with x != 0), # in the future (separate specifiable values for 0/0 and x/0 with x != 0),
@ -707,7 +708,6 @@ def fdiv(a, b, zero_over_zero_value = None):
return np.true_divide(a, b, out = out, where = where) return np.true_divide(a, b, out = out, where = where)
def add_extension_if_not_present(file_name, extension): def add_extension_if_not_present(file_name, extension):
# This could conceivably be smarter about handling existing differing extensions # This could conceivably be smarter about handling existing differing extensions
if(file_name[-len(extension):] != extension): if(file_name[-len(extension):] != extension):

View file

@ -389,6 +389,9 @@ class Mobject(Container):
def stretch_to_fit_height(self, height, **kwargs): def stretch_to_fit_height(self, height, **kwargs):
return self.rescale_to_fit(height, 1, stretch = True, **kwargs) return self.rescale_to_fit(height, 1, stretch = True, **kwargs)
def stretch_to_fit_depth(self, depth, **kwargs):
return self.rescale_to_fit(depth, 1, stretch = True, **kwargs)
def scale_to_fit_width(self, width, **kwargs): def scale_to_fit_width(self, width, **kwargs):
return self.rescale_to_fit(width, 0, stretch = False, **kwargs) return self.rescale_to_fit(width, 0, stretch = False, **kwargs)

View file

@ -37,9 +37,9 @@ class SVGMobject(VMobject):
if self.file_name is None: if self.file_name is None:
raise Exception("Must specify file for SVGMobject") raise Exception("Must specify file for SVGMobject")
possible_paths = [ possible_paths = [
self.file_name,
os.path.join(SVG_IMAGE_DIR, self.file_name), os.path.join(SVG_IMAGE_DIR, self.file_name),
os.path.join(SVG_IMAGE_DIR, self.file_name + ".svg"), os.path.join(SVG_IMAGE_DIR, self.file_name + ".svg"),
self.file_name,
] ]
for path in possible_paths: for path in possible_paths:
if os.path.exists(path): if os.path.exists(path):

View file

@ -82,7 +82,7 @@ class Scene(Container):
def setup(self): def setup(self):
""" """
This is meant to be implement by any scenes which This is meant to be implement by any scenes which
are commonly subclassed, and have some common setup are comonly subclassed, and have some common setup
involved before the construct method is called. involved before the construct method is called.
""" """
pass pass
@ -366,7 +366,6 @@ class Scene(Container):
Each arg can either be an animation, or a mobject method Each arg can either be an animation, or a mobject method
followed by that methods arguments (and potentially follow followed by that methods arguments (and potentially follow
by a dict of kwargs for that method). by a dict of kwargs for that method).
This animation list is built by going through the args list, This animation list is built by going through the args list,
and each animation is simply added, but when a mobject method and each animation is simply added, but when a mobject method
s hit, a MoveToTarget animation is built using the args that s hit, a MoveToTarget animation is built using the args that
@ -387,7 +386,7 @@ class Scene(Container):
animations.pop() animations.pop()
#method should already have target then. #method should already have target then.
else: else:
mobject.target = mobject.deepcopy() mobject.generate_target()
# #
if len(state["method_args"]) > 0 and isinstance(state["method_args"][-1], dict): if len(state["method_args"]) > 0 and isinstance(state["method_args"][-1], dict):
method_kwargs = state["method_args"].pop() method_kwargs = state["method_args"].pop()
@ -578,17 +577,12 @@ class Scene(Container):
FFMPEG_BIN, FFMPEG_BIN,
'-y', # overwrite output file if it exists '-y', # overwrite output file if it exists
'-f', 'rawvideo', '-f', 'rawvideo',
'-vcodec','rawvideo',
'-s', '%dx%d'%(width, height), # size of one frame '-s', '%dx%d'%(width, height), # size of one frame
'-pix_fmt', 'rgba', '-pix_fmt', 'rgba',
'-r', str(fps), # frames per second '-r', str(fps), # frames per second
'-i', '-', # The imput comes from a pipe '-i', '-', # The imput comes from a pipe
'-an', # Tells FFMPEG not to expect any audio '-an', # Tells FFMPEG not to expect any audio
'-vcodec', 'mpeg',
'-c:v', 'libx264',
'-pix_fmt', 'yuv420p',
'-loglevel', 'error', '-loglevel', 'error',
temp_file_path,
] ]
if self.movie_file_extension == ".mov": if self.movie_file_extension == ".mov":
# This is if the background of the exported video # This is if the background of the exported video
@ -624,4 +618,3 @@ class EndSceneEarlyException(Exception):

View file

@ -123,8 +123,11 @@ class PatreonEndScreen(PatreonThanks):
"n_patron_columns" : 3, "n_patron_columns" : 3,
"max_patron_width" : 3, "max_patron_width" : 3,
"run_time" : 20, "run_time" : 20,
"randomize_order" : True,
} }
def construct(self): def construct(self):
if self.randomize_order:
random.shuffle(self.specific_patrons)
self.add_title() self.add_title()
self.scroll_through_patrons() self.scroll_through_patrons()
@ -141,7 +144,6 @@ class PatreonEndScreen(PatreonThanks):
pi.next_to(title, vect, buff = MED_LARGE_BUFF) pi.next_to(title, vect, buff = MED_LARGE_BUFF)
self.add_foreground_mobjects(title, randy, morty) self.add_foreground_mobjects(title, randy, morty)
def scroll_through_patrons(self): def scroll_through_patrons(self):
logo_box = Square(side_length = 2.5) logo_box = Square(side_length = 2.5)
logo_box.to_corner(DOWN+LEFT, buff = MED_LARGE_BUFF) logo_box.to_corner(DOWN+LEFT, buff = MED_LARGE_BUFF)
@ -176,7 +178,7 @@ class PatreonEndScreen(PatreonThanks):
aligned_edge = UP, aligned_edge = UP,
) )
columns.scale_to_fit_width(total_width - 1) columns.scale_to_fit_width(total_width - 1)
columns.next_to(black_rect, DOWN, LARGE_BUFF) columns.next_to(black_rect, DOWN, 3*LARGE_BUFF)
columns.to_edge(RIGHT) columns.to_edge(RIGHT)
self.play( self.play(

View file

@ -128,7 +128,6 @@ class LightSource(VMobject):
self.camera_mob = new_cam_mob self.camera_mob = new_cam_mob
self.spotlight.camera_mob = new_cam_mob self.spotlight.camera_mob = new_cam_mob
def set_screen(self, new_screen): def set_screen(self, new_screen):
if self.has_screen(): if self.has_screen():
self.spotlight.screen = new_screen self.spotlight.screen = new_screen
@ -160,9 +159,6 @@ class LightSource(VMobject):
# in any case # in any case
self.screen = new_screen self.screen = new_screen
def move_source_to(self,point): def move_source_to(self,point):
apoint = np.array(point) apoint = np.array(point)
v = apoint - self.get_source_point() v = apoint - self.get_source_point()
@ -195,14 +191,13 @@ class LightSource(VMobject):
self.spotlight.update_sectors() self.spotlight.update_sectors()
self.update_shadow() self.update_shadow()
def update_lighthouse(self): def update_lighthouse(self):
new_lh = Lighthouse() self.lighthouse.move_to(self.get_source_point())
new_lh.move_to(ORIGIN) # new_lh = Lighthouse()
new_lh.apply_matrix(self.rotation_matrix()) # new_lh.move_to(ORIGIN)
new_lh.shift(self.get_source_point()) # new_lh.apply_matrix(self.rotation_matrix())
self.lighthouse.submobjects = new_lh.submobjects # new_lh.shift(self.get_source_point())
# self.lighthouse.submobjects = new_lh.submobjects
def update_ambient(self): def update_ambient(self):
new_ambient_light = AmbientLight( new_ambient_light = AmbientLight(
@ -217,12 +212,9 @@ class LightSource(VMobject):
new_ambient_light.move_source_to(self.get_source_point()) new_ambient_light.move_source_to(self.get_source_point())
self.ambient_light.submobjects = new_ambient_light.submobjects self.ambient_light.submobjects = new_ambient_light.submobjects
def get_source_point(self): def get_source_point(self):
return self.source_point.get_location() return self.source_point.get_location()
def rotation_matrix(self): def rotation_matrix(self):
if self.camera_mob == None: if self.camera_mob == None:
@ -247,9 +239,7 @@ class LightSource(VMobject):
R = np.dot(R2, R1) R = np.dot(R2, R1)
return R return R
def update_shadow(self): def update_shadow(self):
point = self.get_source_point() point = self.get_source_point()
projected_screen_points = [] projected_screen_points = []
if not self.has_screen(): if not self.has_screen():
@ -319,7 +309,6 @@ class LightSource(VMobject):
self.shadow.mark_paths_closed = True self.shadow.mark_paths_closed = True
class SwitchOn(LaggedStart): class SwitchOn(LaggedStart):
CONFIG = { CONFIG = {
"lag_ratio": 0.2, "lag_ratio": 0.2,
@ -329,9 +318,9 @@ class SwitchOn(LaggedStart):
def __init__(self, light, **kwargs): def __init__(self, light, **kwargs):
if (not isinstance(light,AmbientLight) and not isinstance(light,Spotlight)): if (not isinstance(light,AmbientLight) and not isinstance(light,Spotlight)):
raise Exception("Only AmbientLights and Spotlights can be switched on") raise Exception("Only AmbientLights and Spotlights can be switched on")
LaggedStart.__init__(self, LaggedStart.__init__(
FadeIn, light, **kwargs) self, FadeIn, light, **kwargs
)
class SwitchOff(LaggedStart): class SwitchOff(LaggedStart):
CONFIG = { CONFIG = {
@ -347,9 +336,6 @@ class SwitchOff(LaggedStart):
FadeOut, light, **kwargs) FadeOut, light, **kwargs)
light.submobjects = light.submobjects[::-1] light.submobjects = light.submobjects[::-1]
class Lighthouse(SVGMobject): class Lighthouse(SVGMobject):
CONFIG = { CONFIG = {
"file_name" : "lighthouse", "file_name" : "lighthouse",
@ -361,7 +347,6 @@ class Lighthouse(SVGMobject):
def move_to(self,point): def move_to(self,point):
self.next_to(point, DOWN, buff = 0) self.next_to(point, DOWN, buff = 0)
class AmbientLight(VMobject): class AmbientLight(VMobject):
# Parameters are: # Parameters are:
@ -377,8 +362,8 @@ class AmbientLight(VMobject):
"opacity_function" : lambda r : 1.0/(r+1.0)**2, "opacity_function" : lambda r : 1.0/(r+1.0)**2,
"color" : LIGHT_COLOR, "color" : LIGHT_COLOR,
"max_opacity" : 1.0, "max_opacity" : 1.0,
"num_levels" : 10, "num_levels" : NUM_LEVELS,
"radius" : 10.0 "radius" : 5.0
} }
def generate_points(self): def generate_points(self):
@ -438,21 +423,7 @@ class AmbientLight(VMobject):
submob.set_fill(opacity = new_submob_alpha) submob.set_fill(opacity = new_submob_alpha)
class Spotlight(VMobject): class Spotlight(VMobject):
CONFIG = { CONFIG = {
"source_point": VectorizedPoint(location = ORIGIN, stroke_width = 0, fill_opacity = 0), "source_point": VectorizedPoint(location = ORIGIN, stroke_width = 0, fill_opacity = 0),
"opacity_function" : lambda r : 1.0/(r/2+1.0)**2, "opacity_function" : lambda r : 1.0/(r/2+1.0)**2,
@ -481,13 +452,10 @@ class Spotlight(VMobject):
w = project_along_vector(point,v) w = project_along_vector(point,v)
return w return w
def get_source_point(self): def get_source_point(self):
return self.source_point.get_location() return self.source_point.get_location()
def generate_points(self): def generate_points(self):
self.submobjects = [] self.submobjects = []
self.add(self.source_point) self.add(self.source_point)
@ -503,13 +471,7 @@ class Spotlight(VMobject):
new_sector = self.new_sector(r,dr,lower_angle,upper_angle) new_sector = self.new_sector(r,dr,lower_angle,upper_angle)
self.add(new_sector) self.add(new_sector)
def new_sector(self,r,dr,lower_angle,upper_angle): def new_sector(self,r,dr,lower_angle,upper_angle):
# Note: I'm not looking _too_ closely at the implementation
# of these updates based on viewing angles and such. It seems to
# behave as intended, but let me know if you'd like more thorough
# scrutiny
alpha = self.max_opacity * self.opacity_function(r) alpha = self.max_opacity * self.opacity_function(r)
annular_sector = AnnularSector( annular_sector = AnnularSector(
inner_radius = r, inner_radius = r,
@ -545,7 +507,6 @@ class Spotlight(VMobject):
else: else:
return -absolute_angle return -absolute_angle
def viewing_angles(self,screen): def viewing_angles(self,screen):
screen_points = screen.get_anchors() screen_points = screen.get_anchors()
@ -572,7 +533,6 @@ class Spotlight(VMobject):
return lower_ray, upper_ray return lower_ray, upper_ray
def opening_angle(self): def opening_angle(self):
l,u = self.viewing_angles(self.screen) l,u = self.viewing_angles(self.screen)
return u - l return u - l
@ -592,21 +552,20 @@ class Spotlight(VMobject):
self.update_sectors() self.update_sectors()
return self return self
def update_sectors(self): def update_sectors(self):
if self.screen == None: if self.screen == None:
return return
for submob in self.submobject_family(): for submob in self.submobjects:
if type(submob) == AnnularSector: if type(submob) == AnnularSector:
lower_angle, upper_angle = self.viewing_angles(self.screen) lower_angle, upper_angle = self.viewing_angles(self.screen)
#dr = submob.outer_radius - submob.inner_radius #dr = submob.outer_radius - submob.inner_radius
dr = self.radius / self.num_levels dr = self.radius / self.num_levels
new_submob = self.new_sector(submob.inner_radius,dr,lower_angle,upper_angle) new_submob = self.new_sector(
submob.points = new_submob.points submob.inner_radius, dr, lower_angle, upper_angle
submob.set_fill(opacity = 10 * self.opacity_function(submob.outer_radius)) )
# submob.points = new_submob.points
# submob.set_fill(opacity = 10 * self.opacity_function(submob.outer_radius))
Transform(submob, new_submob).update(1)
def dimming(self,new_alpha): def dimming(self,new_alpha):
old_alpha = self.max_opacity old_alpha = self.max_opacity
@ -640,7 +599,27 @@ class Spotlight(VMobject):
class ScreenTracker(ContinualAnimation): class ScreenTracker(ContinualAnimation):
def __init__(self, light_source, **kwargs):
self.light_source = light_source
dummy_mob = Mobject()
ContinualAnimation.__init__(self, dummy_mob, **kwargs)
def update_mobject(self, dt): def update_mobject(self, dt):
self.mobject.update() self.light_source.update()

View file

@ -144,7 +144,6 @@ class ContinualChangingDecimal(ContinualAnimation):
def update_mobject(self, dt): def update_mobject(self, dt):
self.anim.update(self.internal_time) self.anim.update(self.internal_time)