From 5718db026e2cdc23ada625bd0fc436c334d93ac0 Mon Sep 17 00:00:00 2001 From: mirefek Date: Sun, 28 Jan 2018 14:52:11 +0100 Subject: [PATCH 01/13] bugfix: svg handling --- mobject/svg_mobject.py | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/mobject/svg_mobject.py b/mobject/svg_mobject.py index 5c237a19..dc2273bb 100644 --- a/mobject/svg_mobject.py +++ b/mobject/svg_mobject.py @@ -154,7 +154,7 @@ class SVGMobject(VMobject): transform = string_to_numbers(transform) transform = np.array(transform).reshape([3,2]) x += transform[2][0] - y += transform[2][1] + y -= transform[2][1] matrix = np.identity(self.dim) matrix[:2,:2] = transform[:2,:] t_matrix = np.transpose(matrix) @@ -227,16 +227,25 @@ class VMobjectFromSVGPathstring(VMobject): #list. This variable may get modified in the conditionals below. points = self.growing_path.points new_points = self.string_to_points(coord_string) + + if command == "M": #moveto + if isLower and len(points) > 0: + new_points[0] += points[-1] + if len(points) > 0: + self.growing_path = self.add_subpath(new_points[:1]) + else: + self.growing_path.start_at(new_points[0]) + + if len(new_points) <= 1: return + + points = self.growing_path.points + new_points = new_points[1:] + command = "L" + if isLower and len(points) > 0: new_points += points[-1] - if command == "M": #moveto - if len(points) > 0: - self.growing_path = self.add_subpath(new_points) - else: - if isLower: self.growing_path.start_at(np.sum(new_points, axis=0)) - else: self.growing_path.start_at(new_points[-1]) - return - elif command in ["L", "H", "V"]: #lineto + + if command in ["L", "H", "V"]: #lineto if command == "H": new_points[0,1] = points[-1,1] elif command == "V": From fd1f92b705fb6ae3b5c172aa00d4ec7befcdf318 Mon Sep 17 00:00:00 2001 From: mirefek Date: Sun, 28 Jan 2018 14:55:17 +0100 Subject: [PATCH 02/13] svg can handle ellipse --- mobject/svg_mobject.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/mobject/svg_mobject.py b/mobject/svg_mobject.py index dc2273bb..81d7c7c8 100644 --- a/mobject/svg_mobject.py +++ b/mobject/svg_mobject.py @@ -76,6 +76,8 @@ class SVGMobject(VMobject): result.append(self.rect_to_mobject(element)) elif element.tagName == 'circle': result.append(self.circle_to_mobject(element)) + elif element.tagName == 'ellipse': + result.append(self.ellipse_to_mobject(element)) elif element.tagName in ['polygon', 'polyline']: result.append(self.polygon_to_mobject(element)) else: @@ -122,6 +124,15 @@ class SVGMobject(VMobject): ] return Circle(radius = r).shift(x*RIGHT+y*DOWN) + def ellipse_to_mobject(self, circle_element): + x, y, rx, ry = [ + float(circle_element.getAttribute(key)) + if circle_element.hasAttribute(key) + else 0.0 + for key in "cx", "cy", "rx", "ry" + ] + return Circle().scale(rx*RIGHT + ry*UP).shift(x*RIGHT+y*DOWN) + def rect_to_mobject(self, rect_element): if rect_element.hasAttribute("fill"): if Color(str(rect_element.getAttribute("fill"))) == Color(WHITE): From 8cf2214d15d6cf7dd37c5f80ccbeebe10b7e8e67 Mon Sep 17 00:00:00 2001 From: mirefek Date: Sun, 28 Jan 2018 15:02:57 +0100 Subject: [PATCH 03/13] Feature: SVGMobject("unpack_groups" = False) preserve the hierarchy of groups in the file. --- mobject/svg_mobject.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/mobject/svg_mobject.py b/mobject/svg_mobject.py index 81d7c7c8..0b3b0b6c 100644 --- a/mobject/svg_mobject.py +++ b/mobject/svg_mobject.py @@ -1,7 +1,7 @@ from xml.dom import minidom import warnings -from vectorized_mobject import VMobject +from vectorized_mobject import VMobject, VGroup from topics.geometry import Rectangle, Circle from helpers import * @@ -21,6 +21,7 @@ class SVGMobject(VMobject): "width" : None, #Must be filled in in a subclass, or when called "file_name" : None, + "unpack_groups" : True, # if False, creates a hierarchy of VGroups "stroke_width" : 0, "fill_opacity" : 1, # "fill_color" : LIGHT_GREY, @@ -50,7 +51,9 @@ class SVGMobject(VMobject): doc = minidom.parse(self.file_path) self.ref_to_element = {} for svg in doc.getElementsByTagName("svg"): - self.add(*self.get_mobjects_from(svg)) + mobjects = self.get_mobjects_from(svg) + if self.unpack_groups: self.add(*mobjects) + else: self.add(*mobjects[0].submobjects) doc.unlink() def get_mobjects_from(self, element): @@ -85,6 +88,9 @@ class SVGMobject(VMobject): # warnings.warn("Unknown element type: " + element.tagName) result = filter(lambda m : m is not None, result) self.handle_transforms(element, VMobject(*result)) + if len(result) > 1 and not self.unpack_groups: + result = [VGroup(*result)] + return result def g_to_mobjects(self, g_element): From b7091273392ce4975a8b8533a5d424d4319115a1 Mon Sep 17 00:00:00 2001 From: mirefek Date: Sun, 28 Jan 2018 15:07:29 +0100 Subject: [PATCH 04/13] Bugfix: VMobject.pointwise_become_partial did not worked properly when 0 Date: Sun, 28 Jan 2018 15:28:39 +0100 Subject: [PATCH 05/13] Feature: fine-tuned VGroup: allowing (nested) tuples or lists in constructor VGroup([a,b,c]) = VGroup(a,b,c) VGroup([a,b], c) = VGroup(VGroup(a,b), c) ... --- mobject/vectorized_mobject.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/mobject/vectorized_mobject.py b/mobject/vectorized_mobject.py index 7456223c..439851f2 100644 --- a/mobject/vectorized_mobject.py +++ b/mobject/vectorized_mobject.py @@ -426,8 +426,18 @@ class VMobject(Mobject): return self class VGroup(VMobject): - #Alternate name to improve readability during use - pass + def __init__(self, *args, **kwargs): + if len(args) == 1 and isinstance(args[0], (tuple, list)): + args = args[0] + + packed_args = [] + for arg in args: + if isinstance(arg, (tuple, list)): + packed_args.append(VGroup(arg)) + else: packed_args.append(arg) + + VMobject.__init__(self, *packed_args, **kwargs) + class VectorizedPoint(VMobject): CONFIG = { From 700d62c7ca24b82eb2b4126e0685051c62144af2 Mon Sep 17 00:00:00 2001 From: mirefek Date: Sun, 28 Jan 2018 16:53:17 +0100 Subject: [PATCH 06/13] Feature: BraceLabel, BraceText -- Packed brace with its label + several useful methods --- topics/objects.py | 50 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 44 insertions(+), 6 deletions(-) diff --git a/topics/objects.py b/topics/objects.py index 7fc5d69b..b4643dec 100644 --- a/topics/objects.py +++ b/topics/objects.py @@ -521,19 +521,57 @@ class Broadcast(LaggedStart): ) +class BraceLabel(VMobject): + CONFIG = { + "label_constructor" : TexMobject, + "label_scale" : 1, + } + def __init__(self, obj, text, brace_direction = DOWN, **kwargs): + VMobject.__init__(self, **kwargs) + self.brace_direction = brace_direction + if isinstance(obj, list): obj = VMobject(*obj) + self.brace = Brace(obj, brace_direction, **kwargs) + if isinstance(text, tuple) or isinstance(text, list): + self.label = self.label_constructor(*text, **kwargs) + else: self.label = self.label_constructor(str(text)) + if self.label_scale != 1: self.label.scale(self.label_scale) + self.brace.put_at_tip(self.label) + self.submobjects = [self.brace, self.label] + def creation_anim(self, label_anim = FadeIn, brace_anim = GrowFromCenter): + return AnimationGroup(brace_anim(self.brace), label_anim(self.label)) + def shift_brace(self, obj, **kwargs): + if isinstance(obj, list): obj = VMobject(*obj) + self.brace = Brace(obj, self.brace_direction, **kwargs) + self.brace.put_at_tip(self.label) + self.submobjects[0] = self.brace + return self + def change_label(self, *text, **kwargs): + self.label = self.label_constructor(*text, **kwargs) + if self.label_scale != 1: self.label.scale(self.label_scale) + self.brace.put_at_tip(self.label) + self.submobjects[1] = self.label + return self + def change_brace_label(self, obj, *text): + self.shift_brace(obj) + self.change_label(*text) + return self + def copy(self): + copy_mobject = copy.copy(self) + copy_mobject.brace = self.brace.copy() + copy_mobject.label = self.label.copy() + copy_mobject.submobjects = [copy_mobject.brace, copy_mobject.label] + return copy_mobject - - - - - - +class BraceText(BraceLabel): + CONFIG = { + "label_constructor" : TextMobject + } From edc0fe7589af157f075ab37eb2438f381c9f50bc Mon Sep 17 00:00:00 2001 From: mirefek Date: Sun, 28 Jan 2018 16:56:38 +0100 Subject: [PATCH 07/13] clean_up for AnimationGroup --- animation/simple_animations.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/animation/simple_animations.py b/animation/simple_animations.py index 566cd9f7..411520c5 100644 --- a/animation/simple_animations.py +++ b/animation/simple_animations.py @@ -460,3 +460,9 @@ class AnimationGroup(Animation): def update_mobject(self, alpha): for anim in self.sub_anims: anim.update(alpha) + + def clean_up(self, surrounding_scene = None): + if surrounding_scene is not None: + surrounding_scene.mobjects.remove(self.everything) + for anim in self.sub_anims: + anim.clean_up(surrounding_scene = surrounding_scene) From 3d9f370bceadf2ba104f7b32c31b574de388a013 Mon Sep 17 00:00:00 2001 From: mirefek Date: Sun, 28 Jan 2018 20:31:55 +0100 Subject: [PATCH 08/13] Feature: DashedMObject --- topics/objects.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/topics/objects.py b/topics/objects.py index b4643dec..e5e8a602 100644 --- a/topics/objects.py +++ b/topics/objects.py @@ -575,3 +575,22 @@ class BraceText(BraceLabel): CONFIG = { "label_constructor" : TextMobject } + +class DashedMobject(VMobject): + CONFIG = { + "dashes_num" : 15, + "spacing" : 0.5, + "color" : WHITE + } + def __init__(self, mob, **kwargs): + digest_locals(self) + VMobject.__init__(self, **kwargs) + + buff = float(self.spacing) / self.dashes_num + + for i in range(self.dashes_num): + a = ((1+buff) * i)/self.dashes_num + b = 1-((1+buff) * (self.dashes_num-1-i)) / self.dashes_num + dash = VMobject(color = self.color) + dash.pointwise_become_partial(mob, a, b) + self.submobjects.append(dash) From 7f97616c426bc632cd9a405d0e2c188391b99150 Mon Sep 17 00:00:00 2001 From: mirefek Date: Sun, 28 Jan 2018 20:49:02 +0100 Subject: [PATCH 09/13] Feature: Scene.wait_to(time) Can be useful for audio fitting, ignored in preview --- extract_scene.py | 1 + scene/scene.py | 11 +++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/extract_scene.py b/extract_scene.py index 2580ac8f..66c9f4d6 100644 --- a/extract_scene.py +++ b/extract_scene.py @@ -85,6 +85,7 @@ def get_configuration(): #If -t is passed in (for transparent), this will be RGBA "saved_image_mode": "RGBA" if args.transparent else "RGB", "quiet" : args.quiet or args.write_all, + "ignore_waits" : args.preview, "write_all" : args.write_all, "output_name" : args.output_name, "skip_to_animation_number" : args.skip_to_animation_number, diff --git a/scene/scene.py b/scene/scene.py index fbf2d2ff..1f0afc72 100644 --- a/scene/scene.py +++ b/scene/scene.py @@ -28,6 +28,7 @@ class Scene(object): "frame_duration" : LOW_QUALITY_FRAME_DURATION, "construct_args" : [], "skip_animations" : False, + "ignore_waits" : False, "write_to_movie" : False, "save_frames" : False, "save_pngs" : False, @@ -49,6 +50,7 @@ class Scene(object): self.saved_frames = [] self.shared_locals = {} self.frame_num = 0 + self.current_scene_time = 0 if self.name is None: self.name = self.__class__.__name__ if self.random_seed is not None: @@ -465,6 +467,7 @@ class Scene(object): return self def add_frames(self, *frames): + self.current_scene_time += len(frames)*self.frame_duration if self.write_to_movie: for frame in frames: if self.save_pngs: @@ -551,6 +554,10 @@ class Scene(object): else: os.rename(*self.args_to_rename_file) + def wait_to(self, time, assert_positive = True): + if self.ignore_waits: return + time -= self.current_scene_time + if assert_positive: assert(time >= 0) + elif time < 0: return - - + self.dither(time) From acb709a725825adbd16be90dfaf91f0a54b10216 Mon Sep 17 00:00:00 2001 From: mirefek Date: Sun, 28 Jan 2018 20:50:46 +0100 Subject: [PATCH 10/13] Specify Python version --- extract_scene.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extract_scene.py b/extract_scene.py index 66c9f4d6..825f0ea1 100644 --- a/extract_scene.py +++ b/extract_scene.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 import sys # import getopt From a9c1498affddf9beba9b347d5fd1b1b73c3bba9f Mon Sep 17 00:00:00 2001 From: cclauss Date: Mon, 29 Jan 2018 10:25:43 +0100 Subject: [PATCH 11/13] Resolve undefined names --- old_projects/nn/network.py | 6 ++++++ old_projects/number_line_scene.py | 16 ++++++---------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/old_projects/nn/network.py b/old_projects/nn/network.py index 3ec390da..2aa3aab4 100644 --- a/old_projects/nn/network.py +++ b/old_projects/nn/network.py @@ -29,6 +29,12 @@ IMAGE_MAP_DATA_FILE = os.path.join(NN_DIRECTORY, "image_map") # DEFAULT_LAYER_SIZES = [28**2, 80, 10] DEFAULT_LAYER_SIZES = [28**2, 16, 16, 10] +try: + xrange # Python 2 +except NameError: + xrange = range # Python 3 + + class Network(object): def __init__(self, sizes, non_linearity = "sigmoid"): """The list ``sizes`` contains the number of neurons in the diff --git a/old_projects/number_line_scene.py b/old_projects/number_line_scene.py index d81c1bcd..5054f455 100644 --- a/old_projects/number_line_scene.py +++ b/old_projects/number_line_scene.py @@ -1,6 +1,10 @@ import numpy as np -from animation.transform import Transform +from animation.transform import ApplyMethod, Transform +from constants import RIGHT, SPACE_WIDTH, UP +from helpers import counterclockwise_path, straight_path from point_cloud_mobject import Point +from scene import Scene +from topics.geometry import Line from topics.number_line import NumberLine class NumberLineScene(Scene): @@ -23,7 +27,7 @@ class NumberLineScene(Scene): number_at_center = number ) new_displayed_numbers = new_number_line.default_numbers_to_display() - new_number_mobs = new_number_line.get_number_mobjects(*new_displayed_numbers) + new_number_mobs = new_number_line.get_number_mobjects(*new_displayed_numbers) transforms = [] additional_mobjects = [] @@ -78,11 +82,3 @@ class NumberLineScene(Scene): ApplyMethod(mob.shift, (num-1)*mob.get_center()[0]*RIGHT, **kwargs) for mob in self.number_mobs ]) - - - - - - - - From 7ad6ccaa49bbd5cbfcc9892609fca5a2aba6708a Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Mon, 29 Jan 2018 21:29:24 -0800 Subject: [PATCH 12/13] Bug fixes to 3d display --- topics/three_dimensions.py | 42 +++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/topics/three_dimensions.py b/topics/three_dimensions.py index 9a5faed1..022a213b 100644 --- a/topics/three_dimensions.py +++ b/topics/three_dimensions.py @@ -27,10 +27,10 @@ class CameraWithPerspective(Camera): class ThreeDCamera(CameraWithPerspective): CONFIG = { "sun_vect" : 5*UP+LEFT, - "shading_factor" : 0.5, + "shading_factor" : 0.2, "distance" : 5, "phi" : 0, #Angle off z axis - "theta" : -np.pi/2, #Rotation about z axis + "theta" : -TAU/4, #Rotation about z axis } def __init__(self, *args, **kwargs): Camera.__init__(self, *args, **kwargs) @@ -78,19 +78,26 @@ class ThreeDCamera(CameraWithPerspective): return normal/length def display_multiple_vectorized_mobjects(self, vmobjects): + camera_point = self.spherical_coords_to_point( + *self.get_spherical_coords() + ) def z_cmp(*vmobs): - #Compare to three dimensional mobjects based on their - #z value, otherwise don't compare. - three_d_status = map(should_shade_in_3d, vmobs) - has_points = [vm.get_num_points() > 0 for vm in vmobs] - if all(three_d_status) and all(has_points): - cmp_vect = self.get_unit_normal_vect(vmobs[1]) - return cmp(*[ - np.dot(vm.get_center(), cmp_vect) - for vm in vmobs - ]) - else: - return 0 + #Compare to three dimensional mobjects based on + #how close they are to the camera + return cmp(*[ + -np.linalg.norm(vm.get_center()-camera_point) + for vm in vmobs + ]) + # three_d_status = map(should_shade_in_3d, vmobs) + # has_points = [vm.get_num_points() > 0 for vm in vmobs] + # if all(three_d_status) and all(has_points): + # cmp_vect = self.get_unit_normal_vect(vmobs[1]) + # return cmp(*[ + # np.dot(vm.get_center(), cmp_vect) + # for vm in vmobs + # ]) + # else: + # return 0 Camera.display_multiple_vectorized_mobjects( self, sorted(vmobjects, cmp = z_cmp) ) @@ -176,9 +183,10 @@ class ThreeDScene(Scene): self.add(self.ambient_camera_rotation) def get_moving_mobjects(self, *animations): - if self.camera.rotation_mobject in moving: - return self.mobjects - return Scene.get_moving_mobjects(self, *animations) + moving_mobjects = Scene.get_moving_mobjects(self, *animations) + if self.camera.rotation_mobject in moving_mobjects: + return list_update(self.mobjects, moving_mobjects) + return moving_mobjects ############## From e1390692bef9b8108ff961d96f771f418f331864 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Mon, 29 Jan 2018 21:29:36 -0800 Subject: [PATCH 13/13] Tiny cleanup --- scene/scene.py | 19 +++++++++++-------- topics/objects.py | 2 +- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/scene/scene.py b/scene/scene.py index 9171a45d..76869622 100644 --- a/scene/scene.py +++ b/scene/scene.py @@ -457,6 +457,17 @@ class Scene(Container): return self + def wait_to(self, time, assert_positive = True): + if self.ignore_waits: + return + time -= self.current_scene_time + if assert_positive: + assert(time >= 0) + elif time < 0: + return + + self.wait(time) + def force_skipping(self): self.original_skipping_status = self.skip_animations self.skip_animations = True @@ -554,11 +565,3 @@ class Scene(Container): shutil.move(*self.args_to_rename_file) else: os.rename(*self.args_to_rename_file) - - def wait_to(self, time, assert_positive = True): - if self.ignore_waits: return - time -= self.current_scene_time - if assert_positive: assert(time >= 0) - elif time < 0: return - - self.dither(time) diff --git a/topics/objects.py b/topics/objects.py index e5e8a602..a91c2b33 100644 --- a/topics/objects.py +++ b/topics/objects.py @@ -7,7 +7,7 @@ from mobject.tex_mobject import TextMobject, TexMobject from animation import Animation from animation.simple_animations import Rotating, LaggedStart -from animation.transform import ApplyMethod +from animation.transform import ApplyMethod, FadeIn, GrowFromCenter from topics.geometry import Circle, Line, Rectangle, Square, \ Arc, Polygon, SurroundingRectangle