From 963956cca71e66127488e9edff222566eaa69093 Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Tue, 23 Jan 2018 21:09:53 +0100 Subject: [PATCH 01/20] resolved merge conflicts (#85) --- topics/numerals.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/topics/numerals.py b/topics/numerals.py index d3601839..1970e6de 100644 --- a/topics/numerals.py +++ b/topics/numerals.py @@ -11,7 +11,7 @@ class DecimalNumber(VMobject): "num_decimal_points" : 2, "digit_to_digit_buff" : 0.05, "show_ellipsis" : False, - "unit" : None + "unit" : None, } def __init__(self, number, **kwargs): digest_config(self, kwargs, locals()) @@ -27,6 +27,8 @@ class DecimalNumber(VMobject): if self.show_ellipsis: self.add(TexMobject("\\dots")) + if self.unit is not None: + self.add(TexMobject(self.unit)) self.arrange_submobjects( buff = self.digit_to_digit_buff, @@ -40,7 +42,6 @@ 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, From ea18d984ce6a76c2be4e2da60db8c2b175aeeeac Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Wed, 24 Jan 2018 18:50:01 +0100 Subject: [PATCH 02/20] Not my changes, but I have to commit them?! --- active_projects/fourier.py | 45 ++++++++++++++++++++++++++++++++--- helpers.py | 33 ++++++++++++++++++------- mobject/vectorized_mobject.py | 8 ++++--- 3 files changed, 71 insertions(+), 15 deletions(-) diff --git a/active_projects/fourier.py b/active_projects/fourier.py index f51029f9..395d834c 100644 --- a/active_projects/fourier.py +++ b/active_projects/fourier.py @@ -2455,18 +2455,57 @@ class WhiteComplexExponentialExpression(DrawFrequencyPlot): self.generate_center_of_mass_dot_update_anim() self.add(graph, pol_graph, wps_label) + self.set_variables_as_attrs(pol_graph, wps_label) + self.time_axes_group = VGroup(self.time_axes, graph) def show_winding_with_both_coordinates(self): - #TODO, tie dashed lines to dot + com_dot = self.center_of_mass_dot + plane = self.circle_plane + v_line = Line(ORIGIN, UP) + h_line = Line(ORIGIN, RIGHT) + lines = VGroup(v_line, h_line) + lines.highlight(PINK) + def lines_update(lines): + point = com_dot.get_center() + x, y = plane.point_to_coords(point) + h_line.put_start_and_end_on( + plane.coords_to_point(0, y), point + ) + v_line.put_start_and_end_on( + plane.coords_to_point(x, 0), point + ) + lines_update_anim = UpdateFromFunc(lines, lines_update) + lines_update_anim.update(0) + self.add(lines) self.change_frequency( - 2.0, run_time = 15, + 2.04, + added_anims = [ + lines_update_anim, + self.center_of_mass_dot_anim, + ], + run_time = 15, rate_func = bezier([0, 0, 1, 1]) ) self.wait() + self.dot_component_anim = lines_update_anim + def show_plane_as_complex_plane(self): - pass + to_fade = VGroup( + self.time_axes_group, self.pol_graph, self.wps_label + ) + plane = self.circle_plane + complex_plane_title = TextMobject("Complex plane") + complex_plane_title.add_background_rectangle() + complex_plane_title.to_edge(UP) + coordinate_labels = plane.get_coordinate_labels() + + self.play(FadeOut(to_fade)) + self.play(Write(complex_plane_title)) + self.play(Write(coordinate_labels)) + self.wait() + def show_eulers_formula(self): pass diff --git a/helpers.py b/helpers.py index 079b0b5e..85ad584e 100644 --- a/helpers.py +++ b/helpers.py @@ -252,14 +252,14 @@ def get_all_descendent_classes(Class): result.append(Child) return result -def filtered_locals(local_args): - result = local_args.copy() +def filtered_locals(caller_locals): + result = caller_locals.copy() ignored_local_args = ["self", "kwargs"] for arg in ignored_local_args: - result.pop(arg, local_args) + result.pop(arg, caller_locals) return result -def digest_config(obj, kwargs, local_args = {}): +def digest_config(obj, kwargs, caller_locals = {}): """ Sets init args and CONFIG values as local variables @@ -268,19 +268,25 @@ def digest_config(obj, kwargs, local_args = {}): be easily passed into instantiation, and is attached as an attribute of the object. """ - ### Assemble list of CONFIGs from all super classes + + # Assemble list of CONFIGs from all super classes classes_in_hierarchy = [obj.__class__] - configs = [] + static_configs = [] while len(classes_in_hierarchy) > 0: Class = classes_in_hierarchy.pop() classes_in_hierarchy += Class.__bases__ if hasattr(Class, "CONFIG"): - configs.append(Class.CONFIG) + static_configs.append(Class.CONFIG) #Order matters a lot here, first dicts have higher priority - all_dicts = [kwargs, filtered_locals(local_args), obj.__dict__] - all_dicts += configs + caller_locals = filtered_locals(caller_locals) + all_dicts = [kwargs, caller_locals, obj.__dict__] + all_dicts += static_configs + all_new_dicts = [kwargs, caller_locals] + static_configs obj.__dict__ = merge_config(all_dicts) + #Keep track of the configuration of objects upon + #instantiation + obj.initial_config = merge_config(all_new_dicts) def merge_config(all_dicts): all_config = reduce(op.add, [d.items() for d in all_dicts]) @@ -295,6 +301,15 @@ def merge_config(all_dicts): config[key] = merge_config([config[key], value]) return config +def soft_dict_update(d1, d2): + """ + Adds key values pairs of d2 to d1 only when d1 doesn't + already have that key + """ + for key, value in d2.items(): + if key not in d1: + d1[key] = value + def digest_locals(obj, keys = None): caller_locals = filtered_locals( inspect.currentframe().f_back.f_locals diff --git a/mobject/vectorized_mobject.py b/mobject/vectorized_mobject.py index ec1da178..21194a71 100644 --- a/mobject/vectorized_mobject.py +++ b/mobject/vectorized_mobject.py @@ -85,15 +85,17 @@ class VMobject(Mobject): return self def match_style(self, vmobject): - #TODO: Should this be smart about matching the - #style of the family members, if they happen to - #be different? self.set_style_data( stroke_color = vmobject.get_stroke_color(), stroke_width = vmobject.get_stroke_width(), fill_color = vmobject.get_fill_color(), fill_opacity = vmobject.get_fill_opacity(), + family = False ) + #TODO: This behaviro may not be optimal when submobject + #lists dont' have the same length + for sm1, sm2 in zip(self.submobjects, vmobject.submobjects): + sm1.match_style(sm2) return def fade(self, darkness = 0.5): From a4a146a54eecd57b33feb4a4c86e930ef73cc28c Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Wed, 24 Jan 2018 18:50:44 +0100 Subject: [PATCH 03/20] Merge branch 'master' into lighthouse # Conflicts: # topics/numerals.py --- mobject/mobject.py | 25 +++++++++++--- mobject/vectorized_mobject.py | 12 +++++-- scene/scene.py | 3 +- topics/geometry.py | 6 ---- topics/numerals.py | 64 ++++++++++++++++++++++++----------- 5 files changed, 75 insertions(+), 35 deletions(-) diff --git a/mobject/mobject.py b/mobject/mobject.py index 6e135c95..c8bfc5d3 100644 --- a/mobject/mobject.py +++ b/mobject/mobject.py @@ -316,14 +316,29 @@ class Mobject(object): self.shift(target_point - point_to_align + buff*direction) return self - def align_to(self, mobject_or_point, direction = UP): + def align_to(self, mobject_or_point, direction = ORIGIN, alignment_vect = UP): + """ + Examples: + mob1.align_to(mob2, UP) moves mob1 vertically so that its + top edge lines ups with mob2's top edge. + + mob1.align_to(mob2, alignment_vector = RIGHT) moves mob1 + horizontally so that it's center is directly above/below + the center of mob2 + """ if isinstance(mobject_or_point, Mobject): mob = mobject_or_point - point = mob.get_edge_center(direction) + target_point = mob.get_critical_point(direction) else: - point = mobject_or_point - diff = point - self.get_edge_center(direction) - self.shift(direction*np.dot(diff, direction)) + target_point = mobject_or_point + direction_norm = np.linalg.norm(direction) + if direction_norm > 0: + alignment_vect = np.array(direction)/direction_norm + reference_point = self.get_critical_point(direction) + else: + reference_point = self.get_center() + diff = target_point - reference_point + self.shift(alignment_vect*np.dot(diff, alignment_vect)) return self def shift_onto_screen(self, **kwargs): diff --git a/mobject/vectorized_mobject.py b/mobject/vectorized_mobject.py index 21194a71..264181fd 100644 --- a/mobject/vectorized_mobject.py +++ b/mobject/vectorized_mobject.py @@ -92,9 +92,15 @@ class VMobject(Mobject): fill_opacity = vmobject.get_fill_opacity(), family = False ) - #TODO: This behaviro may not be optimal when submobject - #lists dont' have the same length - for sm1, sm2 in zip(self.submobjects, vmobject.submobjects): + + #Does its best to match up submobject lists, and + #match styles accordingly + submobs1, submobs2 = self.submobjects, vmobject.submobjects + if len(submobs1) == 0: + return + elif len(submobs2) == 0: + submobs2 = [vmobject] + for sm1, sm2 in zip(*make_even(submobs1, submobs2)): sm1.match_style(sm2) return diff --git a/scene/scene.py b/scene/scene.py index fb4ba6ca..fbf2d2ff 100644 --- a/scene/scene.py +++ b/scene/scene.py @@ -482,7 +482,7 @@ class Scene(object): def preview(self): TkSceneRoot(self) - + def get_image_file_path(self, name = None, dont_update = False): folder = "images" if dont_update: @@ -540,7 +540,6 @@ class Scene(object): '-loglevel', 'error', temp_file_path, ] - # self.writing_process = sp.Popen(command, stdin=sp.PIPE, shell=True) self.writing_process = sp.Popen(command, stdin=sp.PIPE) diff --git a/topics/geometry.py b/topics/geometry.py index 0ef70f20..bbbd12a5 100644 --- a/topics/geometry.py +++ b/topics/geometry.py @@ -172,18 +172,12 @@ class AnnularSector(VMobject): arc_center = first_point - self.inner_radius * radial_unit_vector return arc_center -<<<<<<< HEAD def move_arc_center_to(self,point): v = point - self.get_arc_center() self.shift(v) return self - - -======= ->>>>>>> master class Sector(AnnularSector): - CONFIG = { "outer_radius" : 1, "inner_radius" : 0 diff --git a/topics/numerals.py b/topics/numerals.py index 8125436b..d5a9bae3 100644 --- a/topics/numerals.py +++ b/topics/numerals.py @@ -3,6 +3,7 @@ from mobject.vectorized_mobject import VMobject, VGroup, VectorizedPoint from mobject.tex_mobject import TexMobject from animation import Animation from animation.continual_animation import ContinualAnimation +from topics.geometry import BackgroundRectangle from scene import Scene from helpers import * @@ -12,18 +13,31 @@ class DecimalNumber(VMobject): "digit_to_digit_buff" : 0.05, "show_ellipsis" : False, "unit" : None, + "include_background_rectangle" : False, } def __init__(self, number, **kwargs): - digest_config(self, kwargs, locals()) - num_string = '%.*f'%(self.num_decimal_points, number) - negative_zero_string = "-%.*f"%(self.num_decimal_points, 0.) - if num_string == negative_zero_string: - num_string = num_string[1:] - VMobject.__init__(self, *[ - TexMobject(char) - for char in num_string - ], **kwargs) + VMobject.__init__(self, **kwargs) + self.number = number + ndp = self.num_decimal_points + #Build number string + if isinstance(number, complex): + num_string = '%.*f%s%.*fi'%( + ndp, number.real, + "-" if number.imag < 0 else "+", + ndp, abs(number.imag) + ) + else: + num_string = '%.*f'%(ndp, number) + negative_zero_string = "-%.*f"%(ndp, 0.) + if num_string == negative_zero_string: + num_string = num_string[1:] + self.add(*[ + TexMobject(char, **kwargs) + for char in num_string + ]) + + #Add non-numerical bits if self.show_ellipsis: self.add(TexMobject("\\dots")) @@ -35,6 +49,7 @@ class DecimalNumber(VMobject): aligned_edge = DOWN ) +<<<<<<< HEAD if num_string.startswith("-"): minus = self.submobjects[0] minus.next_to( @@ -55,18 +70,29 @@ class DecimalNumber(VMobject): class Integer(VGroup): +======= + #Handle alignment of parts that should be aligned + #to the bottom + for i, c in enumerate(num_string): + if c == "-" and len(num_string) > i+1: + self[i].align_to(self[i+1], alignment_vect = UP) + if self.unit == "\\circ": + self[-1].align_to(self, UP) + # + if self.include_background_rectangle: + #TODO, is this the best way to handle + #background rectangles? + self.background_rectangle = BackgroundRectangle(self) + self.submobjects = [ + self.background_rectangle, + VGroup(*self.submobjects) + ] + +class Integer(DecimalNumber): +>>>>>>> master CONFIG = { - "digit_buff" : 0.8*SMALL_BUFF + "num_decimal_points" : 0, } - def __init__(self, integer, **kwargs): - self.number = integer - num_str = str(integer) - VGroup.__init__(self, *map(TexMobject, num_str), **kwargs) - self.arrange_submobjects( - RIGHT, buff = self.digit_buff, aligned_edge = DOWN - ) - if num_str[0] == "-": - self[0].next_to(self[1], LEFT, buff = SMALL_BUFF) class ChangingDecimal(Animation): CONFIG = { From afc0c0d5ae7aad57cf03beb0f1a651c38c33fd99 Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Thu, 25 Jan 2018 23:23:04 +0100 Subject: [PATCH 04/20] Trying to make light cones update correctly --- active_projects/basel.py | 64 +++++++++++++++++++++++++++------------- 1 file changed, 43 insertions(+), 21 deletions(-) diff --git a/active_projects/basel.py b/active_projects/basel.py index 909fd1cc..b6263cd0 100644 --- a/active_projects/basel.py +++ b/active_projects/basel.py @@ -48,6 +48,7 @@ LIGHT_CONE_NUM_SECTORS = 30 NUM_CONES = 50 # in first lighthouse scene NUM_VISIBLE_CONES = 5 # ibidem ARC_TIP_LENGTH = 0.2 +DIM_OPACITY = 0.2 def show_line_length(line): @@ -95,6 +96,7 @@ class LightScreen(VMobject): 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 @@ -133,6 +135,11 @@ 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) + + class LightCone(VGroup): CONFIG = { @@ -173,6 +180,7 @@ class LightCone(VGroup): 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 @@ -754,27 +762,31 @@ class SingleLightHouseScene(PiCreatureScene): 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), - ) + # 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), + # ) + lc_updater = lambda lc: light_screen.update_light_cone(lc) sh_updater = lambda sh: light_screen.update_shadow(sh) ca1 = ContinualUpdateFromFunc(light_screen.light_cone, lc_updater) + ca15 = ContinualUpdateFromFunc(light_screen, + lc_updater) ca2 = ContinualUpdateFromFunc(light_screen.shadow, sh_updater) - self.add(ca1, ca2) + self.add(ca1, ca15, ca2) self.add_foreground_mobject(morty) - pointing_screen_at_source = ApplyMethod(screen.rotate_in_place,TAU/6) - self.play(pointing_screen_at_source) + pointing_screen_at_source = ApplyMethod(screen.rotate,TAU/6) + #self.play(pointing_screen_at_source) + #self.wait() arc_angle = light_cone.angle # draw arc arrows to show the opening angle @@ -791,7 +803,7 @@ 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: light_cone.angle/TAU * 360 ca3 = ContinualChangingDecimal(angle_indicator,angle_update_func) self.add(ca3) @@ -799,8 +811,18 @@ class SingleLightHouseScene(PiCreatureScene): 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 = ApplyMethod(light_screen.screen.rotate, + TAU/8, run_time=1.5) + #self.wait(2) + rotating_screen_2 = ApplyMethod(light_screen.screen.rotate, + -TAU/4, run_time=3, rate_func = there_and_back) + #self.wait(2) + rotating_screen_3 = ApplyMethod(light_screen.screen.rotate, + TAU/8, run_time=1.5) + + #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) @@ -815,14 +837,14 @@ class SingleLightHouseScene(PiCreatureScene): 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]) + #ApplyMethod(lighthouse.move_to,sun_position), + #ApplyMethod(candle.move_to,sun_position), + ApplyMethod(light_screen.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]) ) From 9e57ac2be6109ee5101f4fa73e89431a75aa5938 Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Thu, 25 Jan 2018 23:23:43 +0100 Subject: [PATCH 05/20] Annulus: symmetrized code --- topics/geometry.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/topics/geometry.py b/topics/geometry.py index bbbd12a5..b1152f0d 100644 --- a/topics/geometry.py +++ b/topics/geometry.py @@ -203,10 +203,12 @@ class Annulus(Circle): } def generate_points(self): + self.points = [] self.radius = self.outer_radius - Circle.generate_points(self) + outer_circle = Circle(radius = self.outer_radius) inner_circle = Circle(radius=self.inner_radius) inner_circle.flip() + self.points = outer_circle.points self.add_subpath(inner_circle.points) class Line(VMobject): From 3589a292cb5ffd0839fc3e06d1cf14fc7edf1fd8 Mon Sep 17 00:00:00 2001 From: bhbr Date: Fri, 26 Jan 2018 19:33:22 +0100 Subject: [PATCH 06/20] gitignore --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index ea54dace..f0f3ca3f 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,7 @@ .DS_Store homeless.py ka_playgrounds/ -grant_playground.py +playground.py special_animations.py prettiness_hall_of_fame.py files/ From 2fd0687b596f47c0c2bfde4f1fa9b6ba5e3c2f2f Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Fri, 26 Jan 2018 19:49:15 +0100 Subject: [PATCH 07/20] Added my playground files (for syncing btw my machines) --- .gitignore | 3 - ben_cairo_test.py | 54 +++++++++ ben_playground.py | 303 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 357 insertions(+), 3 deletions(-) create mode 100644 ben_cairo_test.py create mode 100644 ben_playground.py diff --git a/.gitignore b/.gitignore index f0f3ca3f..29cac87a 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,3 @@ playground.py special_animations.py prettiness_hall_of_fame.py files/ -ben_playground.py - -ben_cairo_test.py diff --git a/ben_cairo_test.py b/ben_cairo_test.py new file mode 100644 index 00000000..85af2269 --- /dev/null +++ b/ben_cairo_test.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python +"""demonstrate pycairo and pygame""" + +from __future__ import print_function + +import math +import sys + +import cairo +import pygame + + +def draw(surface): + x, y, radius = (250, 250, 200) + ctx = cairo.Context(surface) + ctx.set_line_width(15) + ctx.arc(x, y, radius, 0, 2.0 * math.pi) + ctx.set_source_rgb(0.8, 0.8, 0.8) + ctx.fill_preserve() + ctx.set_source_rgb(1, 1, 1) + ctx.stroke() + + +def input(events): + for event in events: + if event.type == pygame.QUIT: + sys.exit(0) + else: + print(event) + + +def main(): + width, height = 512, 512 + surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height) + + pygame.init() + pygame.display.set_mode((width, height)) + screen = pygame.display.get_surface() + + draw(surface) + + # Create PyGame surface from Cairo Surface + buf = surface.get_data() + image = pygame.image.frombuffer(buf, (width, height), "ARGB") + # Tranfer to Screen + screen.blit(image, (0, 0)) + pygame.display.flip() + + while True: + input(pygame.event.get()) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/ben_playground.py b/ben_playground.py new file mode 100644 index 00000000..1b7445fd --- /dev/null +++ b/ben_playground.py @@ -0,0 +1,303 @@ +#!/usr/bin/env python + +from helpers import * + +from mobject.tex_mobject import TexMobject +from mobject import Mobject +from mobject.image_mobject import ImageMobject +from mobject.vectorized_mobject import * +from mobject.point_cloud_mobject import PointCloudDot + +from animation.animation import Animation +from animation.transform import * +from animation.simple_animations import * +from animation.continual_animation import * +from animation.playground import * + +from topics.geometry import * +from topics.characters import * +from topics.functions import * +from topics.number_line import * +from topics.combinatorics import * +from scene import Scene +from camera import Camera +from mobject.svg_mobject import * +from mobject.tex_mobject import * + +from mobject.vectorized_mobject import * + +## To watch one of these scenes, run the following: +## python extract_scene.py -p file_name + +LIGHT_COLOR = YELLOW +DEGREES = 360/TAU +SWITCH_ON_RUN_TIME = 1.5 + + +class AmbientLight(VMobject): + + # Parameters are: + # * a source point + # * possibly a target + # * an opacity function and its inverse + # * a light color + # * a max opacity + + # If the target is None, create annuli. + # If there is a target, create annular sectors. + + 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): + + for submob in self.submobjects: + self.remove(submob) + + # create annuli + 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) + + # else: + + # # look for the screen and create annular sectors + # lower_angle, upper_angle = self.viewing_angles(self.screen) + # 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) + + + + + + # def redraw(self): + # if self.screen != None: + # lower_angle, upper_angle = self.viewing_angles(self.screen) + # for submob in self.submobjects: + # if type(submob) == AnnularSector: + # submob.start_angle = lower_angle + # submob.angle = upper_angle - lower_angle + # submob.generate_points() + # submob.move_arc_center_to(self.source_point) + + + #submob.generate_points() + + + def move_source_to(self,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+1.0)**2, + "color" : LIGHT_COLOR, + "max_opacity" : 1.0, + "num_levels" : 10, + "radius" : 5.0, + "screen" : None + } + + 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) + 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) + + + def viewing_angle_of_point(self,point): + distance_vector = point - self.source_point + angle = angle_of_vector(distance_vector) + return angle + + def viewing_angles(self,screen): + + 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 move_source_to(self,point): + self.source_point = np.array(point) + self.shift(np.array(point) - self.source_point) + self.generate_points() + + 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 SwitchOn(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") + 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 LightSource(Mobject): + +# # A light source is composed of: +# # * a lighthouse +# # * possibly a screen +# # * and two light fields: +# # * an undirected one (annuli) +# # * one directed at the screen (annular sectors) + +# CONFIG = { +# "location" : ORIGIN, +# "icon" : SVGMobject(file_name = 'lighthouse', height = 0.5), +# "ambient_light" : LightField(), +# "spot_light_field" : LightField(), +# } + +# def __init__(self,**kwargs): +# Mobject.__init__(self,**kwargs) +# self.icon.next_to(self.location, DOWN, buff = 0) +# self.spot_light_field.max_opacity = 0 +# self.ambient_light_field.move_source_to(self.location) +# self.spot_light_field.move_source_to(self.location) +# self.add(self.icon,self.ambient_light_field,self.spot_light_field) + +# def dim_ambient(self,new_alpha): +# self.ambient_light_field.dimming(new_alpha) + + + +class ScreenTracker(ContinualAnimation): + def __init__(self, spotlight, screen, **kwargs): + self.spotlight = spotlight + self.screen = screen + ContinualAnimation.__init__(self, self.spotlight, **kwargs) + +# def update_mobject(self, dt): + #self.spotlight.generate_points() + + + + +class IntroScene(Scene): + def construct(self): + + screen = Square().shift([4,0,0]) + #ambient_light = AmbientLight( + # source_point = np.array([-1,1,0]), + # max_opacity = 1.0, + # opacity_function = lambda r: 1.0/(r/1+1)**2, + # num_levels = 10, + #) + + #ambient_light.move_source_to([-5,0,0]) + self.add(screen)#,ambient_light) + + spotlight = Spotlight( + source_point = np.array([-1,1,0]), + max_opacity = 1.0, + opacity_function = lambda r: 1.0/(r/2+1)**2, + num_levels = 10, + screen = screen, + ) + + self.add(spotlight) + + ca = ScreenTracker(spotlight,screen) + self.add(ca) + + #self.play(SwitchOn(ambient_light)) + #self.play(ApplyMethod(ambient_light.move_source_to,[-3,1,0])) + #self.play(SwitchOn(spotlight)) + #self.play(ApplyMethod(spotlight.move_source_to,[-3,-1,0])) + #self.play(ApplyMethod(spotlight.dimming,0.2)) + self.play(screen.rotate, TAU/8, run_time = 3) + self.play(ApplyMethod(spotlight.move_source_to,[-4,0,0])) + + self.wait() + + + From daadc857c45ab608a8815f45b635def9fcc8c70d Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Fri, 26 Jan 2018 19:50:12 +0100 Subject: [PATCH 08/20] Revert "Added my playground files (for syncing btw my machines)" This reverts commit 2fd0687b596f47c0c2bfde4f1fa9b6ba5e3c2f2f. --- .gitignore | 3 + ben_cairo_test.py | 54 --------- ben_playground.py | 303 ---------------------------------------------- 3 files changed, 3 insertions(+), 357 deletions(-) delete mode 100644 ben_cairo_test.py delete mode 100644 ben_playground.py diff --git a/.gitignore b/.gitignore index 29cac87a..f0f3ca3f 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,6 @@ playground.py special_animations.py prettiness_hall_of_fame.py files/ +ben_playground.py + +ben_cairo_test.py diff --git a/ben_cairo_test.py b/ben_cairo_test.py deleted file mode 100644 index 85af2269..00000000 --- a/ben_cairo_test.py +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env python -"""demonstrate pycairo and pygame""" - -from __future__ import print_function - -import math -import sys - -import cairo -import pygame - - -def draw(surface): - x, y, radius = (250, 250, 200) - ctx = cairo.Context(surface) - ctx.set_line_width(15) - ctx.arc(x, y, radius, 0, 2.0 * math.pi) - ctx.set_source_rgb(0.8, 0.8, 0.8) - ctx.fill_preserve() - ctx.set_source_rgb(1, 1, 1) - ctx.stroke() - - -def input(events): - for event in events: - if event.type == pygame.QUIT: - sys.exit(0) - else: - print(event) - - -def main(): - width, height = 512, 512 - surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height) - - pygame.init() - pygame.display.set_mode((width, height)) - screen = pygame.display.get_surface() - - draw(surface) - - # Create PyGame surface from Cairo Surface - buf = surface.get_data() - image = pygame.image.frombuffer(buf, (width, height), "ARGB") - # Tranfer to Screen - screen.blit(image, (0, 0)) - pygame.display.flip() - - while True: - input(pygame.event.get()) - - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/ben_playground.py b/ben_playground.py deleted file mode 100644 index 1b7445fd..00000000 --- a/ben_playground.py +++ /dev/null @@ -1,303 +0,0 @@ -#!/usr/bin/env python - -from helpers import * - -from mobject.tex_mobject import TexMobject -from mobject import Mobject -from mobject.image_mobject import ImageMobject -from mobject.vectorized_mobject import * -from mobject.point_cloud_mobject import PointCloudDot - -from animation.animation import Animation -from animation.transform import * -from animation.simple_animations import * -from animation.continual_animation import * -from animation.playground import * - -from topics.geometry import * -from topics.characters import * -from topics.functions import * -from topics.number_line import * -from topics.combinatorics import * -from scene import Scene -from camera import Camera -from mobject.svg_mobject import * -from mobject.tex_mobject import * - -from mobject.vectorized_mobject import * - -## To watch one of these scenes, run the following: -## python extract_scene.py -p file_name - -LIGHT_COLOR = YELLOW -DEGREES = 360/TAU -SWITCH_ON_RUN_TIME = 1.5 - - -class AmbientLight(VMobject): - - # Parameters are: - # * a source point - # * possibly a target - # * an opacity function and its inverse - # * a light color - # * a max opacity - - # If the target is None, create annuli. - # If there is a target, create annular sectors. - - 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): - - for submob in self.submobjects: - self.remove(submob) - - # create annuli - 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) - - # else: - - # # look for the screen and create annular sectors - # lower_angle, upper_angle = self.viewing_angles(self.screen) - # 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) - - - - - - # def redraw(self): - # if self.screen != None: - # lower_angle, upper_angle = self.viewing_angles(self.screen) - # for submob in self.submobjects: - # if type(submob) == AnnularSector: - # submob.start_angle = lower_angle - # submob.angle = upper_angle - lower_angle - # submob.generate_points() - # submob.move_arc_center_to(self.source_point) - - - #submob.generate_points() - - - def move_source_to(self,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+1.0)**2, - "color" : LIGHT_COLOR, - "max_opacity" : 1.0, - "num_levels" : 10, - "radius" : 5.0, - "screen" : None - } - - 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) - 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) - - - def viewing_angle_of_point(self,point): - distance_vector = point - self.source_point - angle = angle_of_vector(distance_vector) - return angle - - def viewing_angles(self,screen): - - 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 move_source_to(self,point): - self.source_point = np.array(point) - self.shift(np.array(point) - self.source_point) - self.generate_points() - - 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 SwitchOn(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") - 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 LightSource(Mobject): - -# # A light source is composed of: -# # * a lighthouse -# # * possibly a screen -# # * and two light fields: -# # * an undirected one (annuli) -# # * one directed at the screen (annular sectors) - -# CONFIG = { -# "location" : ORIGIN, -# "icon" : SVGMobject(file_name = 'lighthouse', height = 0.5), -# "ambient_light" : LightField(), -# "spot_light_field" : LightField(), -# } - -# def __init__(self,**kwargs): -# Mobject.__init__(self,**kwargs) -# self.icon.next_to(self.location, DOWN, buff = 0) -# self.spot_light_field.max_opacity = 0 -# self.ambient_light_field.move_source_to(self.location) -# self.spot_light_field.move_source_to(self.location) -# self.add(self.icon,self.ambient_light_field,self.spot_light_field) - -# def dim_ambient(self,new_alpha): -# self.ambient_light_field.dimming(new_alpha) - - - -class ScreenTracker(ContinualAnimation): - def __init__(self, spotlight, screen, **kwargs): - self.spotlight = spotlight - self.screen = screen - ContinualAnimation.__init__(self, self.spotlight, **kwargs) - -# def update_mobject(self, dt): - #self.spotlight.generate_points() - - - - -class IntroScene(Scene): - def construct(self): - - screen = Square().shift([4,0,0]) - #ambient_light = AmbientLight( - # source_point = np.array([-1,1,0]), - # max_opacity = 1.0, - # opacity_function = lambda r: 1.0/(r/1+1)**2, - # num_levels = 10, - #) - - #ambient_light.move_source_to([-5,0,0]) - self.add(screen)#,ambient_light) - - spotlight = Spotlight( - source_point = np.array([-1,1,0]), - max_opacity = 1.0, - opacity_function = lambda r: 1.0/(r/2+1)**2, - num_levels = 10, - screen = screen, - ) - - self.add(spotlight) - - ca = ScreenTracker(spotlight,screen) - self.add(ca) - - #self.play(SwitchOn(ambient_light)) - #self.play(ApplyMethod(ambient_light.move_source_to,[-3,1,0])) - #self.play(SwitchOn(spotlight)) - #self.play(ApplyMethod(spotlight.move_source_to,[-3,-1,0])) - #self.play(ApplyMethod(spotlight.dimming,0.2)) - self.play(screen.rotate, TAU/8, run_time = 3) - self.play(ApplyMethod(spotlight.move_source_to,[-4,0,0])) - - self.wait() - - - From 698373c428a9986bae2106e2b98aa8f7c13dfa24 Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Fri, 26 Jan 2018 19:51:08 +0100 Subject: [PATCH 09/20] Added my playground for syncing btw my machines --- .gitignore | 3 --- 1 file changed, 3 deletions(-) diff --git a/.gitignore b/.gitignore index f0f3ca3f..29cac87a 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,3 @@ playground.py special_animations.py prettiness_hall_of_fame.py files/ -ben_playground.py - -ben_cairo_test.py From 49ed0ab4f48ad0071f65644dbaa52bb966a4c12c Mon Sep 17 00:00:00 2001 From: bhbr Date: Fri, 26 Jan 2018 19:56:49 +0100 Subject: [PATCH 10/20] Added my playground --- ben_playground.py | 303 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 303 insertions(+) create mode 100644 ben_playground.py diff --git a/ben_playground.py b/ben_playground.py new file mode 100644 index 00000000..1b7445fd --- /dev/null +++ b/ben_playground.py @@ -0,0 +1,303 @@ +#!/usr/bin/env python + +from helpers import * + +from mobject.tex_mobject import TexMobject +from mobject import Mobject +from mobject.image_mobject import ImageMobject +from mobject.vectorized_mobject import * +from mobject.point_cloud_mobject import PointCloudDot + +from animation.animation import Animation +from animation.transform import * +from animation.simple_animations import * +from animation.continual_animation import * +from animation.playground import * + +from topics.geometry import * +from topics.characters import * +from topics.functions import * +from topics.number_line import * +from topics.combinatorics import * +from scene import Scene +from camera import Camera +from mobject.svg_mobject import * +from mobject.tex_mobject import * + +from mobject.vectorized_mobject import * + +## To watch one of these scenes, run the following: +## python extract_scene.py -p file_name + +LIGHT_COLOR = YELLOW +DEGREES = 360/TAU +SWITCH_ON_RUN_TIME = 1.5 + + +class AmbientLight(VMobject): + + # Parameters are: + # * a source point + # * possibly a target + # * an opacity function and its inverse + # * a light color + # * a max opacity + + # If the target is None, create annuli. + # If there is a target, create annular sectors. + + 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): + + for submob in self.submobjects: + self.remove(submob) + + # create annuli + 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) + + # else: + + # # look for the screen and create annular sectors + # lower_angle, upper_angle = self.viewing_angles(self.screen) + # 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) + + + + + + # def redraw(self): + # if self.screen != None: + # lower_angle, upper_angle = self.viewing_angles(self.screen) + # for submob in self.submobjects: + # if type(submob) == AnnularSector: + # submob.start_angle = lower_angle + # submob.angle = upper_angle - lower_angle + # submob.generate_points() + # submob.move_arc_center_to(self.source_point) + + + #submob.generate_points() + + + def move_source_to(self,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+1.0)**2, + "color" : LIGHT_COLOR, + "max_opacity" : 1.0, + "num_levels" : 10, + "radius" : 5.0, + "screen" : None + } + + 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) + 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) + + + def viewing_angle_of_point(self,point): + distance_vector = point - self.source_point + angle = angle_of_vector(distance_vector) + return angle + + def viewing_angles(self,screen): + + 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 move_source_to(self,point): + self.source_point = np.array(point) + self.shift(np.array(point) - self.source_point) + self.generate_points() + + 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 SwitchOn(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") + 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 LightSource(Mobject): + +# # A light source is composed of: +# # * a lighthouse +# # * possibly a screen +# # * and two light fields: +# # * an undirected one (annuli) +# # * one directed at the screen (annular sectors) + +# CONFIG = { +# "location" : ORIGIN, +# "icon" : SVGMobject(file_name = 'lighthouse', height = 0.5), +# "ambient_light" : LightField(), +# "spot_light_field" : LightField(), +# } + +# def __init__(self,**kwargs): +# Mobject.__init__(self,**kwargs) +# self.icon.next_to(self.location, DOWN, buff = 0) +# self.spot_light_field.max_opacity = 0 +# self.ambient_light_field.move_source_to(self.location) +# self.spot_light_field.move_source_to(self.location) +# self.add(self.icon,self.ambient_light_field,self.spot_light_field) + +# def dim_ambient(self,new_alpha): +# self.ambient_light_field.dimming(new_alpha) + + + +class ScreenTracker(ContinualAnimation): + def __init__(self, spotlight, screen, **kwargs): + self.spotlight = spotlight + self.screen = screen + ContinualAnimation.__init__(self, self.spotlight, **kwargs) + +# def update_mobject(self, dt): + #self.spotlight.generate_points() + + + + +class IntroScene(Scene): + def construct(self): + + screen = Square().shift([4,0,0]) + #ambient_light = AmbientLight( + # source_point = np.array([-1,1,0]), + # max_opacity = 1.0, + # opacity_function = lambda r: 1.0/(r/1+1)**2, + # num_levels = 10, + #) + + #ambient_light.move_source_to([-5,0,0]) + self.add(screen)#,ambient_light) + + spotlight = Spotlight( + source_point = np.array([-1,1,0]), + max_opacity = 1.0, + opacity_function = lambda r: 1.0/(r/2+1)**2, + num_levels = 10, + screen = screen, + ) + + self.add(spotlight) + + ca = ScreenTracker(spotlight,screen) + self.add(ca) + + #self.play(SwitchOn(ambient_light)) + #self.play(ApplyMethod(ambient_light.move_source_to,[-3,1,0])) + #self.play(SwitchOn(spotlight)) + #self.play(ApplyMethod(spotlight.move_source_to,[-3,-1,0])) + #self.play(ApplyMethod(spotlight.dimming,0.2)) + self.play(screen.rotate, TAU/8, run_time = 3) + self.play(ApplyMethod(spotlight.move_source_to,[-4,0,0])) + + self.wait() + + + From 3f939248810a39e571bace438c85250943a012eb Mon Sep 17 00:00:00 2001 From: bhbr Date: Fri, 26 Jan 2018 22:05:09 +0100 Subject: [PATCH 11/20] cleanup --- ben_playground.py | 104 ++++++++++------------------------------------ 1 file changed, 22 insertions(+), 82 deletions(-) diff --git a/ben_playground.py b/ben_playground.py index 1b7445fd..f6fe1805 100644 --- a/ben_playground.py +++ b/ben_playground.py @@ -38,13 +38,11 @@ class AmbientLight(VMobject): # Parameters are: # * a source point - # * possibly a target - # * an opacity function and its inverse + # * an opacity function # * a light color # * a max opacity - - # If the target is None, create annuli. - # If there is a target, create annular sectors. + # * a radius (larger than the opacity's dropoff length) + # * the number of subdivisions (levels, annuli) CONFIG = { "source_point" : ORIGIN, @@ -57,6 +55,8 @@ class AmbientLight(VMobject): 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) @@ -73,47 +73,14 @@ class AmbientLight(VMobject): annulus.move_arc_center_to(self.source_point) self.add(annulus) - # else: - - # # look for the screen and create annular sectors - # lower_angle, upper_angle = self.viewing_angles(self.screen) - # 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) - - - - - - # def redraw(self): - # if self.screen != None: - # lower_angle, upper_angle = self.viewing_angles(self.screen) - # for submob in self.submobjects: - # if type(submob) == AnnularSector: - # submob.start_angle = lower_angle - # submob.angle = upper_angle - lower_angle - # submob.generate_points() - # submob.move_arc_center_to(self.source_point) - - - #submob.generate_points() 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()) + # 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 @@ -221,32 +188,6 @@ class SwitchOff(LaggedStart): light.submobjects = light.submobjects[::-1] -# class LightSource(Mobject): - -# # A light source is composed of: -# # * a lighthouse -# # * possibly a screen -# # * and two light fields: -# # * an undirected one (annuli) -# # * one directed at the screen (annular sectors) - -# CONFIG = { -# "location" : ORIGIN, -# "icon" : SVGMobject(file_name = 'lighthouse', height = 0.5), -# "ambient_light" : LightField(), -# "spot_light_field" : LightField(), -# } - -# def __init__(self,**kwargs): -# Mobject.__init__(self,**kwargs) -# self.icon.next_to(self.location, DOWN, buff = 0) -# self.spot_light_field.max_opacity = 0 -# self.ambient_light_field.move_source_to(self.location) -# self.spot_light_field.move_source_to(self.location) -# self.add(self.icon,self.ambient_light_field,self.spot_light_field) - -# def dim_ambient(self,new_alpha): -# self.ambient_light_field.dimming(new_alpha) @@ -266,15 +207,14 @@ class IntroScene(Scene): def construct(self): screen = Square().shift([4,0,0]) - #ambient_light = AmbientLight( - # source_point = np.array([-1,1,0]), - # max_opacity = 1.0, - # opacity_function = lambda r: 1.0/(r/1+1)**2, - # num_levels = 10, - #) + self.add(screen) - #ambient_light.move_source_to([-5,0,0]) - self.add(screen)#,ambient_light) + ambient_light = AmbientLight( + source_point = np.array([-1,1,0]), + max_opacity = 1.0, + opacity_function = lambda r: 1.0/(r/2+1)**2, + num_levels = 10, + ) spotlight = Spotlight( source_point = np.array([-1,1,0]), @@ -284,18 +224,18 @@ class IntroScene(Scene): screen = screen, ) - self.add(spotlight) + #self.add(ambient_light) - ca = ScreenTracker(spotlight,screen) - self.add(ca) + #ca = ScreenTracker(spotlight,screen) + #self.add(ca) - #self.play(SwitchOn(ambient_light)) + self.play(SwitchOn(ambient_light)) #self.play(ApplyMethod(ambient_light.move_source_to,[-3,1,0])) #self.play(SwitchOn(spotlight)) #self.play(ApplyMethod(spotlight.move_source_to,[-3,-1,0])) #self.play(ApplyMethod(spotlight.dimming,0.2)) - self.play(screen.rotate, TAU/8, run_time = 3) - self.play(ApplyMethod(spotlight.move_source_to,[-4,0,0])) + #self.play(screen.rotate, TAU/8, run_time = 3) + #self.play(ApplyMethod(spotlight.move_source_to,[-4,0,0])) self.wait() From a9f3bbd4a3d21a565371a8da4c1bf0591173d157 Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Fri, 26 Jan 2018 19:54:20 +0100 Subject: [PATCH 12/20] Revert "Added my playground for syncing btw my machines" This reverts commit 698373c428a9986bae2106e2b98aa8f7c13dfa24. --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 29cac87a..f0f3ca3f 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,6 @@ playground.py special_animations.py prettiness_hall_of_fame.py files/ +ben_playground.py + +ben_cairo_test.py From c05174990551f46284123d47d74264e0a5ad6b87 Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Sat, 27 Jan 2018 13:29:20 +0100 Subject: [PATCH 13/20] old Integer implem lingering around --- topics/numerals.py | 23 +---------------------- 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/topics/numerals.py b/topics/numerals.py index 0be11a49..e53e20e9 100644 --- a/topics/numerals.py +++ b/topics/numerals.py @@ -54,7 +54,7 @@ class DecimalNumber(VMobject): unit_sign.next_to(self.submobjects[-1],RIGHT, buff = self.digit_to_digit_buff) - if self.unit == "^\\circ": + if self.unit.startswith("^"): unit_sign.align_to(self,UP) else: unit_sign.align_to(self,DOWN) @@ -66,27 +66,6 @@ class DecimalNumber(VMobject): ) -class Integer(VGroup): - #Handle alignment of parts that should be aligned - #to the bottom - for i, c in enumerate(num_string): - if c == "-" and len(num_string) > i+1: - self[i].align_to(self[i+1], alignment_vect = UP) - if self.unit == "\\circ": - self[-1].align_to(self, UP) - # - if self.include_background_rectangle: - self.add_background_rectangle() - - def add_background_rectangle(self): - #TODO, is this the best way to handle - #background rectangles? - self.background_rectangle = BackgroundRectangle(self) - self.submobjects = [ - self.background_rectangle, - VGroup(*self.submobjects) - ] - return self class Integer(DecimalNumber): CONFIG = { From 526929098a6d2416647ce4de36c30bd5f21fecb4 Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Sat, 27 Jan 2018 13:29:39 +0100 Subject: [PATCH 14/20] added method for setting path arc of a Line --- topics/geometry.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/topics/geometry.py b/topics/geometry.py index b5113ee5..b9695f46 100644 --- a/topics/geometry.py +++ b/topics/geometry.py @@ -234,6 +234,10 @@ class Line(VMobject): ]) self.account_for_buff() + def set_path_arc(self,new_value): + self.path_arc = new_value + self.generate_points() + def account_for_buff(self): length = self.get_arc_length() if length < 2*self.buff or self.buff == 0: From 68c08f0a5a123cf3aee928d74c9e16f14fb2a70a Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Sat, 27 Jan 2018 13:30:25 +0100 Subject: [PATCH 15/20] Added working light, scenes 3 and 4 --- active_projects/basel.py | 643 +++++++++++++++++++++++++-------------- 1 file changed, 410 insertions(+), 233 deletions(-) diff --git a/active_projects/basel.py b/active_projects/basel.py index b6263cd0..bfe9cfac 100644 --- a/active_projects/basel.py +++ b/active_projects/basel.py @@ -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 -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): - - - - - - - - - - - From e1edb98997ce0d16a7441616953fbea8dea3f52f Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Sat, 27 Jan 2018 14:07:37 +0100 Subject: [PATCH 16/20] playing with lights --- ben_playground.py | 87 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 66 insertions(+), 21 deletions(-) diff --git a/ben_playground.py b/ben_playground.py index f6fe1805..c03c8abe 100644 --- a/ben_playground.py +++ b/ben_playground.py @@ -100,7 +100,8 @@ class Spotlight(VMobject): "max_opacity" : 1.0, "num_levels" : 10, "radius" : 5.0, - "screen" : None + "screen" : None, + "shadow" : VMobject(fill_color = RED, stroke_width = 0, fill_opacity = 1.0) } def track_screen(self): @@ -128,6 +129,9 @@ class Spotlight(VMobject): 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 viewing_angle_of_point(self,point): distance_vector = point - self.source_point @@ -147,19 +151,54 @@ class Spotlight(VMobject): def move_source_to(self,point): self.source_point = np.array(point) - self.shift(np.array(point) - self.source_point) - self.generate_points() + 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): + print "updating shadow" + use_point = point #self.source_point + self.shadow.points = self.screen.points + 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 + 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 + 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) + class SwitchOn(LaggedStart): CONFIG = { "lag_ratio": 0.2, @@ -192,52 +231,58 @@ class SwitchOff(LaggedStart): class ScreenTracker(ContinualAnimation): - def __init__(self, spotlight, screen, **kwargs): - self.spotlight = spotlight - self.screen = screen - ContinualAnimation.__init__(self, self.spotlight, **kwargs) - -# def update_mobject(self, dt): - #self.spotlight.generate_points() - + 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 IntroScene(Scene): def construct(self): - screen = Square().shift([4,0,0]) + screen = Line([2,-2,0],[1,2,0]).shift([1,0,0]) self.add(screen) ambient_light = AmbientLight( source_point = np.array([-1,1,0]), max_opacity = 1.0, opacity_function = lambda r: 1.0/(r/2+1)**2, - num_levels = 10, + num_levels = 4, ) spotlight = Spotlight( source_point = np.array([-1,1,0]), max_opacity = 1.0, opacity_function = lambda r: 1.0/(r/2+1)**2, - num_levels = 10, + num_levels = 4, screen = screen, ) - #self.add(ambient_light) + self.add(spotlight) - #ca = ScreenTracker(spotlight,screen) + screen_updater = ScreenTracker(spotlight) #self.add(ca) - self.play(SwitchOn(ambient_light)) + #self.play(SwitchOn(ambient_light)) #self.play(ApplyMethod(ambient_light.move_source_to,[-3,1,0])) #self.play(SwitchOn(spotlight)) - #self.play(ApplyMethod(spotlight.move_source_to,[-3,-1,0])) - #self.play(ApplyMethod(spotlight.dimming,0.2)) - #self.play(screen.rotate, TAU/8, run_time = 3) + + self.add(screen_updater) + self.play(ApplyMethod(spotlight.screen.rotate,TAU/8)) + self.remove(screen_updater) + self.play(ApplyMethod(spotlight.move_source_to,[-3,-1,0])) + self.add(screen_updater) + spotlight.source_point = [-3,-1,0] + + self.play(ApplyMethod(spotlight.dimming,0.2)) #self.play(ApplyMethod(spotlight.move_source_to,[-4,0,0])) - self.wait() + #self.wait() From 7ca95b41953c68ef65875c38453cbb818ecb64f7 Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Sat, 27 Jan 2018 15:17:31 +0100 Subject: [PATCH 17/20] added scene 5, but shd be done w light indicators instead --- active_projects/basel.py | 153 ++++++++++++++++++++++++++++----------- 1 file changed, 109 insertions(+), 44 deletions(-) diff --git a/active_projects/basel.py b/active_projects/basel.py index bfe9cfac..8b1497e3 100644 --- a/active_projects/basel.py +++ b/active_projects/basel.py @@ -48,7 +48,8 @@ NUM_LEVELS = 30 NUM_CONES = 50 # in first lighthouse scene NUM_VISIBLE_CONES = 5 # ibidem ARC_TIP_LENGTH = 0.2 -DIM_OPACITY = 0.2 +AMBIENT_FULL = 1.0 +AMBIENT_DIMMED = 0.2 DEGREES = TAU/360 @@ -786,7 +787,7 @@ class SingleLightHouseScene(PiCreatureScene): lighthouse = LightHouse() ambient_light = AmbientLight( - opacity_function = inverse_quadratic(1,2,1), + opacity_function = inverse_quadratic(AMBIENT_FULL,2,1), num_levels = NUM_LEVELS, radius = 10, brightness = 1, @@ -817,7 +818,7 @@ class SingleLightHouseScene(PiCreatureScene): self.play( - ApplyMethod(ambient_light.dimming,0.2), + ApplyMethod(ambient_light.dimming,AMBIENT_DIMMED), FadeIn(spotlight)) self.add(spotlight.shadow) @@ -872,31 +873,29 @@ class SingleLightHouseScene(PiCreatureScene): #self.wait() +### The following is supposed to morph the scene into the Earth scene, +### but it doesn't work - # morph into Earth scene - 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(ambient_light.move_to,sun_position), - ApplyMethod(spotlight.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]) + # # morph into Earth scene - ) - self.play( - ApplyMethod(spotlight.change_opacity_function,new_opacity_function)) + # 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(ambient_light.move_to,sun_position), + # ApplyMethod(spotlight.move_source_to,sun_position), - self.add(screen_tracker) + # ) + # self.play( + # ApplyMethod(spotlight.change_opacity_function,new_opacity_function)) + + # self.add(screen_tracker) @@ -954,7 +953,10 @@ class ScreenShapingScene(Scene): DEGREES = TAU / 360 - screen = Line([2,-1,0],[2,1,0], path_arc = 0, num_arc_anchors = 10) + 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) source = Spotlight( opacity_function = inverse_quadratic(1,5,1), @@ -968,7 +970,7 @@ class ScreenShapingScene(Scene): lighthouse = LightHouse() ambient_light = AmbientLight( - opacity_function = inverse_quadratic(1,1,1), + opacity_function = inverse_quadratic(AMBIENT_DIMMED,1,1), num_levels = NUM_LEVELS, radius = 10, brightness = 1, @@ -1002,17 +1004,22 @@ class ScreenShapingScene(Scene): # in preparation for the slanting, create a rectangle that show the brightness - rect_origin = Rectangle(width = 0, height = 2).move_to(screen.get_center()) + rect_origin = Rectangle(width = 0, height = screen_height).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 = Rectangle(width = brightness_rect_height, + height = brightness_rect_height, fill_color = YELLOW, fill_opacity = 0.5) - brightness_rect.move_to([3,3,0]) + 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() + # for unslanting the screen later + lower_screen_point, upper_screen_point = screen.get_start_and_end() lower_slanted_screen_point = interpolate( @@ -1027,13 +1034,81 @@ class ScreenShapingScene(Scene): 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) + 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.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}), + ReplacementTransform(screen,slanted_screen), + ReplacementTransform(brightness_rect,slanted_brightness_rect), + ) + + self.wait() + + # Scene 5: constant screen size, changing opening angle + + screen = slanted_screen + source.screen = screen + self.remove(slanted_screen) + + brightness_rect = slanted_brightness_rect + self.remove(slanted_brightness_rect) + + + self.play( + ReplacementTransform(screen,original_screen), + ReplacementTransform(brightness_rect,original_brightness_rect), + ) + + self.remove(original_brightness_rect) + + shifted_brightness_rect = brightness_rect.copy() + shifted_brightness_rect.shift([-3,0,0]).set_fill(opacity = 0.8) + + self.play( + ApplyMethod(screen.shift,[-3,0,0]), + ApplyMethod(morty.shift,[-3,0,0]), + #ApplyMethod(brightness_rect.shift,[-3,0,0]), + #ApplyMethod(brightness_rect.set_fill,{"opacity": 0.8}) + ReplacementTransform(brightness_rect,shifted_brightness_rect) + ) + + self.remove(original_screen) # was still hiding behind the shadow + self.remove(shifted_brightness_rect) # also one too many + + # add distance indicator + + left_x = source.source_point[0] + right_x = screen.get_center()[0] + indicator_y = -2 + line1 = Arrow([left_x,indicator_y,0],[right_x,indicator_y,0]) + line2 = Arrow([right_x,indicator_y,0],[left_x,indicator_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,indicator_y,0],[new_right_x,indicator_y,0]) + new_line2 = Arrow([new_right_x,indicator_y,0],[left_x,indicator_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) + + new_brightness_rect = brightness_rect.copy() + new_brightness_rect.shift([distance_to_source,0,0]) + new_brightness_rect.set_fill(opacity = 0.2) + + self.play( + ReplacementTransform(line,new_line), + ApplyMethod(screen.shift,[distance_to_source,0,0]), + Transform(brightness_rect,new_brightness_rect), + ApplyMethod(morty.shift,[distance_to_source,0,0]), ) @@ -1045,13 +1120,3 @@ class ScreenShapingScene(Scene): - - - - - - - - - - From 5169d6b297b0cbe7ef071cf5d0dfeefe3f42b6fb Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Sat, 27 Jan 2018 19:09:29 +0100 Subject: [PATCH 18/20] Scenes 2-5 mostly finished --- active_projects/basel.py | 79 +++++++++++++++++++++++++++------------- 1 file changed, 54 insertions(+), 25 deletions(-) diff --git a/active_projects/basel.py b/active_projects/basel.py index 8b1497e3..71135f30 100644 --- a/active_projects/basel.py +++ b/active_projects/basel.py @@ -241,9 +241,9 @@ class Spotlight(VMobject): 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) + ray1 = rotate_vector(ray1,-TAU/100) ray2 = ray2/np.linalg.norm(ray2) * 100 - ray2 = rotate_vector(ray2,TAU/16) + ray2 = rotate_vector(ray2,TAU/100) outpoint1 = self.screen.points[0] + ray1 outpoint2 = self.screen.points[-1] + ray2 self.shadow.add_control_points([outpoint2,outpoint1,self.screen.points[0]]) @@ -336,7 +336,8 @@ class LightIndicator(Mobject): CONFIG = { "radius": 0.5, "intensity": 0, - "opacity_for_unit_intensity": 1 + "opacity_for_unit_intensity": 1, + "precision": 3 } def generate_points(self): @@ -346,7 +347,7 @@ class LightIndicator(Mobject): self.foreground.set_stroke(color=INDICATOR_STROKE_COLOR,width=INDICATOR_STROKE_WIDTH) self.add(self.background, self.foreground) - self.reading = DecimalNumber(self.intensity,num_decimal_points = 3) + self.reading = DecimalNumber(self.intensity,num_decimal_points = self.precision) self.reading.set_fill(color=INDICATOR_TEXT_COLOR) self.reading.move_to(self.get_center()) self.add(self.reading) @@ -873,7 +874,7 @@ class SingleLightHouseScene(PiCreatureScene): #self.wait() -### The following is supposed to morph the scene into the Earth scene, +### The following is supposed to morph the scene into the Earth scene, ### but it doesn't work @@ -1045,7 +1046,7 @@ class ScreenShapingScene(Scene): self.wait() - # Scene 5: constant screen size, changing opening angle + screen = slanted_screen source.screen = screen @@ -1062,27 +1063,57 @@ class ScreenShapingScene(Scene): self.remove(original_brightness_rect) - shifted_brightness_rect = brightness_rect.copy() - shifted_brightness_rect.shift([-3,0,0]).set_fill(opacity = 0.8) + + # 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 + + # 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) + + indicator = LightIndicator(radius = indicator_height/2, + opacity_for_unit_intensity = OPACITY_FOR_UNIT_INTENSITY, + color = LIGHT_COLOR, + precision = 2) + indicator.set_intensity(indicator_intensity) + + indicator.move_to(slanted_brightness_rect.get_center()) + self.play( - ApplyMethod(screen.shift,[-3,0,0]), - ApplyMethod(morty.shift,[-3,0,0]), - #ApplyMethod(brightness_rect.shift,[-3,0,0]), - #ApplyMethod(brightness_rect.set_fill,{"opacity": 0.8}) - ReplacementTransform(brightness_rect,shifted_brightness_rect) + FadeOut(slanted_brightness_rect), + FadeIn(indicator) + ) + + self.add_foreground_mobject(indicator.reading) + + new_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.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), ) self.remove(original_screen) # was still hiding behind the shadow - self.remove(shifted_brightness_rect) # also one too many - + # add distance indicator left_x = source.source_point[0] right_x = screen.get_center()[0] - indicator_y = -2 - line1 = Arrow([left_x,indicator_y,0],[right_x,indicator_y,0]) - line2 = Arrow([right_x,indicator_y,0],[left_x,indicator_y,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) @@ -1093,21 +1124,19 @@ class ScreenShapingScene(Scene): # move everything away distance_to_source = right_x - left_x new_right_x = left_x + 2*distance_to_source - new_line1 = Arrow([left_x,indicator_y,0],[new_right_x,indicator_y,0]) - new_line2 = Arrow([new_right_x,indicator_y,0],[left_x,indicator_y,0]) + 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) + new_line = VGroup(new_line1, new_line2, new_distance_decimal) - new_brightness_rect = brightness_rect.copy() - new_brightness_rect.shift([distance_to_source,0,0]) - new_brightness_rect.set_fill(opacity = 0.2) self.play( ReplacementTransform(line,new_line), ApplyMethod(screen.shift,[distance_to_source,0,0]), - Transform(brightness_rect,new_brightness_rect), + ApplyMethod(indicator.shift,[left_shift,0,0]), + ApplyMethod(indicator.set_intensity,indicator_intensity), ApplyMethod(morty.shift,[distance_to_source,0,0]), ) From e139d54ba98086d001f82f1e022052a68c422490 Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Sat, 27 Jan 2018 19:09:59 +0100 Subject: [PATCH 19/20] Added Ellipse --- topics/geometry.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/topics/geometry.py b/topics/geometry.py index b9695f46..a802b199 100644 --- a/topics/geometry.py +++ b/topics/geometry.py @@ -128,6 +128,19 @@ class Dot(Circle): self.shift(point) self.init_colors() +class Ellipse(VMobject): + CONFIG = { + "width" : 2, + "height" : 1 + } + + def generate_points(self): + circle = Circle(radius = 1) + circle = circle.stretch_to_fit_width(self.width) + circle = circle.stretch_to_fit_height(self.height) + self.points = circle.points + + class AnnularSector(VMobject): CONFIG = { "inner_radius" : 1, From b38a8dade674b1fc1fb4c991e0fae6f69183579f Mon Sep 17 00:00:00 2001 From: Ben Hambrecht Date: Mon, 29 Jan 2018 15:20:57 +0100 Subject: [PATCH 20/20] refactoring scenes 2-5, started on scene 6 (faux 3D) --- active_projects/basel.py | 596 +++++++++++++++++++++++++++------------ 1 file changed, 410 insertions(+), 186 deletions(-) diff --git a/active_projects/basel.py b/active_projects/basel.py index 71135f30..a29a14bb 100644 --- a/active_projects/basel.py +++ b/active_projects/basel.py @@ -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))