From 983a3e0357d82818795baf44a6c1fb4e2571c65e Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Sat, 10 Feb 2018 21:38:12 -0800 Subject: [PATCH 1/5] Direct computation of color hex to speed things up --- camera/camera.py | 34 +++++++++++++++++++++------------- helpers.py | 2 +- mobject/vectorized_mobject.py | 6 ++++++ 3 files changed, 28 insertions(+), 14 deletions(-) diff --git a/camera/camera.py b/camera/camera.py index 111eae03..6c7c369f 100644 --- a/camera/camera.py +++ b/camera/camera.py @@ -241,14 +241,22 @@ class Camera(object): canvas.symbol((0, 0), symbol, pen, fill) def get_pen_and_fill(self, vmobject): - pen = aggdraw.Pen( - self.color_to_hex_l(self.get_stroke_color(vmobject)), - max(vmobject.stroke_width, 0) - ) - fill = aggdraw.Brush( - self.color_to_hex_l(self.get_fill_color(vmobject)), - opacity = int(self.color_max_val*vmobject.get_fill_opacity()) - ) + stroke_width = max(vmobject.get_stroke_width(), 0) + if stroke_width == 0: + pen = None + else: + stroke_rgb = self.get_stroke_rgb(vmobject) + stroke_hex = rgb_to_hex(stroke_rgb) + pen = aggdraw.Pen(stroke_hex, stroke_width) + + fill_opacity = int(self.color_max_val*vmobject.get_fill_opacity()) + if fill_opacity == 0: + fill = None + else: + fill_rgb = self.get_fill_rgb(vmobject) + fill_hex = rgb_to_hex(fill_rgb) + fill = aggdraw.Brush(fill_hex, fill_opacity) + return (pen, fill) def color_to_hex_l(self, color): @@ -257,14 +265,14 @@ class Camera(object): except: return Color(BLACK).get_hex_l() - def get_stroke_color(self, vmobject): - return vmobject.get_stroke_color() + def get_stroke_rgb(self, vmobject): + return vmobject.get_stroke_rgb() - def get_fill_color(self, vmobject): - return vmobject.get_fill_color() + def get_fill_rgb(self, vmobject): + return vmobject.get_fill_rgb() def get_pathstring(self, vmobject): - result = "" + result = "" for mob in [vmobject]+vmobject.get_subpath_mobjects(): points = mob.points # points = self.adjust_out_of_range_points(points) diff --git a/helpers.py b/helpers.py index 8e1af238..d0e3c962 100644 --- a/helpers.py +++ b/helpers.py @@ -126,7 +126,7 @@ def rgba_to_color(rgba): return rgb_to_color(rgba[:3]) def rgb_to_hex(rgb): - return Color(rgb = rgb).get_hex_l() + return "#" + "".join('%02x'%int(255*x) for x in rgb) def invert_color(color): return rgb_to_color(1.0 - color_to_rgb(color)) diff --git a/mobject/vectorized_mobject.py b/mobject/vectorized_mobject.py index fbf055b0..7bb8bdf3 100644 --- a/mobject/vectorized_mobject.py +++ b/mobject/vectorized_mobject.py @@ -120,6 +120,9 @@ class VMobject(Mobject): ) return self + def get_fill_rgb(self): + return self.fill_rgb + def get_fill_color(self): try: self.fill_rgb = np.clip(self.fill_rgb, 0.0, 1.0) @@ -130,6 +133,9 @@ class VMobject(Mobject): def get_fill_opacity(self): return np.clip(self.fill_opacity, 0, 1) + def get_stroke_rgb(self): + return self.stroke_rgb + def get_stroke_color(self): try: self.stroke_rgb = np.clip(self.stroke_rgb, 0, 1) From c9ff83920cfb4d2604b92ba23d2c71b4f07dc5e0 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Sat, 10 Feb 2018 21:43:55 -0800 Subject: [PATCH 2/5] Updated ThreeDCamera methods for new color scheme --- topics/three_dimensions.py | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/topics/three_dimensions.py b/topics/three_dimensions.py index a7314d2e..55de2925 100644 --- a/topics/three_dimensions.py +++ b/topics/three_dimensions.py @@ -40,22 +40,17 @@ class ThreeDCamera(CameraWithPerspective): self.rotation_mobject = VectorizedPoint() self.set_position(self.phi, self.theta, self.distance) - def get_color(self, method): - color = method() - vmobject = method.im_self + def modified_rgb(self, vmobject, rgb): if should_shade_in_3d(vmobject): - return Color(rgb = self.get_shaded_rgb( - color_to_rgb(color), - normal_vect = self.get_unit_normal_vect(vmobject) - )) + return self.get_shaded_rgb(rgb, self.get_unit_normal_vect(vmobject)) else: return color - def get_stroke_color(self, vmobject): - return self.get_color(vmobject.get_stroke_color) + def get_stroke_rgb(self, vmobject): + return self.modified_rgb(vmobject, vmobject.get_stroke_rgb()) - def get_fill_color(self, vmobject): - return self.get_color(vmobject.get_fill_color) + def get_fill_rgb(self, vmobject): + return self.modified_rgb(vmobject, vmobject.get_fill_rgb()) def get_shaded_rgb(self, rgb, normal_vect): brightness = np.dot(normal_vect, self.unit_sun_vect)**2 From 5e25ecd33ca8651a9a142e9e04b44d70a947cd30 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Sat, 10 Feb 2018 22:19:00 -0800 Subject: [PATCH 3/5] Tiny changes --- animation/transform.py | 2 +- camera/camera.py | 9 ++------- topics/number_line.py | 2 -- 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/animation/transform.py b/animation/transform.py index 30d7c7d2..4ee132a5 100644 --- a/animation/transform.py +++ b/animation/transform.py @@ -48,7 +48,7 @@ class Transform(Animation): self.path_arc, self.path_arc_axis, ) - + def get_all_mobjects(self): return self.mobject, self.starting_mobject, self.target_mobject diff --git a/camera/camera.py b/camera/camera.py index 6c7c369f..958787f9 100644 --- a/camera/camera.py +++ b/camera/camera.py @@ -10,10 +10,6 @@ from helpers import * from mobject import Mobject, PMobject, VMobject, \ ImageMobject, Group, BackgroundColoredVMobject -# Set a @profile decorator over any method whose -# performance you'd like to analyze -from profilehooks import profile - class Camera(object): CONFIG = { "background_image" : None, @@ -179,7 +175,6 @@ class Camera(object): def capture_mobject(self, mobject, **kwargs): return self.capture_mobjects([mobject], **kwargs) - @profile def capture_mobjects(self, mobjects, **kwargs): self.reset_aggdraw_canvas() mobjects = self.get_mobjects_to_display(mobjects, **kwargs) @@ -256,7 +251,7 @@ class Camera(object): fill_rgb = self.get_fill_rgb(vmobject) fill_hex = rgb_to_hex(fill_rgb) fill = aggdraw.Brush(fill_hex, fill_opacity) - + return (pen, fill) def color_to_hex_l(self, color): @@ -280,7 +275,7 @@ class Camera(object): continue aligned_points = self.align_points_to_camera(points) coords = self.points_to_pixel_coords(aligned_points) - coord_strings = coords.flatten().astype("string") + coord_strings = coords.flatten().astype(str) #Start new path string with M coord_strings[0] = "M" + coord_strings[0] #The C at the start of every 6th number communicates diff --git a/topics/number_line.py b/topics/number_line.py index 4dab9f07..30ad693a 100644 --- a/topics/number_line.py +++ b/topics/number_line.py @@ -136,8 +136,6 @@ class NumberLine(VMobject): self.tip = tip self.add(tip) - - class UnitInterval(NumberLine): CONFIG = { "x_min" : 0, From b9ef9f6fc0128a1a4b38c456b2a5d5a712c73a72 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Sat, 10 Feb 2018 22:34:39 -0800 Subject: [PATCH 4/5] Enabled adding end animation number with -n flag --- extract_scene.py | 9 ++++++++- scene/scene.py | 5 +++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/extract_scene.py b/extract_scene.py index 825f0ea1..ea34fa06 100644 --- a/extract_scene.py +++ b/extract_scene.py @@ -89,6 +89,7 @@ def get_configuration(): "write_all" : args.write_all, "output_name" : args.output_name, "skip_to_animation_number" : args.skip_to_animation_number, + "end_after_animation_number" : None, } if args.low_quality: config["camera_config"] = LOW_QUALITY_CAMERA_CONFIG @@ -102,7 +103,12 @@ def get_configuration(): stan = config["skip_to_animation_number"] if stan is not None: - config["skip_to_animation_number"] = int(stan) + if "," in stan: + start, end = stan.split(",") + config["skip_to_animation_number"] = int(start) + config["end_after_animation_number"] = int(end) + else: + config["skip_to_animation_number"] = int(stan) config["skip_animations"] = any([ config["show_last_frame"] and not config["write_to_movie"], @@ -221,6 +227,7 @@ def main(): "output_directory", "save_pngs", "skip_to_animation_number", + "end_after_animation_number", ] ]) diff --git a/scene/scene.py b/scene/scene.py index 4c72897c..625145f4 100644 --- a/scene/scene.py +++ b/scene/scene.py @@ -40,6 +40,7 @@ class Scene(Container): "always_continually_update" : False, "random_seed" : 0, "skip_to_animation_number" : None, + "end_after_animation_number" : None, } def __init__(self, **kwargs): Container.__init__(self, **kwargs) # Perhaps allow passing in a non-empty *mobjects parameter? @@ -409,6 +410,10 @@ class Scene(Container): if self.skip_to_animation_number: if self.num_plays + 1 == self.skip_to_animation_number: self.skip_animations = False + if self.end_after_animation_number: + if self.num_plays >= self.end_after_animation_number: + self.skip_animations = True + return self #Don't even both with the rest... if self.skip_animations: kwargs["run_time"] = 0 From 9cfab5c2ed3ca5370d221f5fffe561f54d6fc386 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Sat, 10 Feb 2018 22:45:46 -0800 Subject: [PATCH 5/5] Better convention for -n flag --- extract_scene.py | 20 ++++++++++---------- scene/scene.py | 14 +++++++------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/extract_scene.py b/extract_scene.py index ea34fa06..605b4513 100644 --- a/extract_scene.py +++ b/extract_scene.py @@ -68,7 +68,7 @@ def get_configuration(): for short_arg, long_arg in optional_args: parser.add_argument(short_arg, long_arg, action = "store_true") parser.add_argument("-o", "--output_name") - parser.add_argument("-n", "--skip_to_animation_number") + parser.add_argument("-n", "--start_at_animation_number") args = parser.parse_args() except argparse.ArgumentError as err: print(str(err)) @@ -88,8 +88,8 @@ def get_configuration(): "ignore_waits" : args.preview, "write_all" : args.write_all, "output_name" : args.output_name, - "skip_to_animation_number" : args.skip_to_animation_number, - "end_after_animation_number" : None, + "start_at_animation_number" : args.start_at_animation_number, + "end_at_animation_number" : None, } if args.low_quality: config["camera_config"] = LOW_QUALITY_CAMERA_CONFIG @@ -101,18 +101,18 @@ def get_configuration(): config["camera_config"] = PRODUCTION_QUALITY_CAMERA_CONFIG config["frame_duration"] = PRODUCTION_QUALITY_FRAME_DURATION - stan = config["skip_to_animation_number"] + stan = config["start_at_animation_number"] if stan is not None: if "," in stan: start, end = stan.split(",") - config["skip_to_animation_number"] = int(start) - config["end_after_animation_number"] = int(end) + config["start_at_animation_number"] = int(start) + config["end_at_animation_number"] = int(end) else: - config["skip_to_animation_number"] = int(stan) + config["start_at_animation_number"] = int(stan) config["skip_animations"] = any([ config["show_last_frame"] and not config["write_to_movie"], - config["skip_to_animation_number"], + config["start_at_animation_number"], ]) return config @@ -226,8 +226,8 @@ def main(): "write_to_movie", "output_directory", "save_pngs", - "skip_to_animation_number", - "end_after_animation_number", + "start_at_animation_number", + "end_at_animation_number", ] ]) diff --git a/scene/scene.py b/scene/scene.py index 625145f4..1cb81bff 100644 --- a/scene/scene.py +++ b/scene/scene.py @@ -39,8 +39,8 @@ class Scene(Container): "name" : None, "always_continually_update" : False, "random_seed" : 0, - "skip_to_animation_number" : None, - "end_after_animation_number" : None, + "start_at_animation_number" : None, + "end_at_animation_number" : None, } def __init__(self, **kwargs): Container.__init__(self, **kwargs) # Perhaps allow passing in a non-empty *mobjects parameter? @@ -407,18 +407,17 @@ class Scene(Container): if len(args) == 0: warnings.warn("Called Scene.play with no animations") return - if self.skip_to_animation_number: - if self.num_plays + 1 == self.skip_to_animation_number: + if self.start_at_animation_number: + if self.num_plays == self.start_at_animation_number: self.skip_animations = False - if self.end_after_animation_number: - if self.num_plays >= self.end_after_animation_number: + if self.end_at_animation_number: + if self.num_plays >= self.end_at_animation_number: self.skip_animations = True return self #Don't even both with the rest... if self.skip_animations: kwargs["run_time"] = 0 animations = self.compile_play_args_to_animation_list(*args) - self.num_plays += 1 sync_animation_run_times_and_rate_funcs(*animations, **kwargs) moving_mobjects = self.get_moving_mobjects(*animations) @@ -434,6 +433,7 @@ class Scene(Container): self.mobjects_from_last_animation = moving_mobjects self.clean_up_animations(*animations) self.continual_update(0) + self.num_plays += 1 return self def clean_up_animations(self, *animations):