diff --git a/animation/simple_animations.py b/animation/simple_animations.py index c151a326..f50d47c6 100644 --- a/animation/simple_animations.py +++ b/animation/simple_animations.py @@ -485,4 +485,4 @@ class AnimationGroup(Animation): def clean_up(self, *args, **kwargs): for anim in self.sub_anims: - anim.clean_up(*args, **kwargs) \ No newline at end of file + anim.clean_up(*args, **kwargs) diff --git a/extract_scene.py b/extract_scene.py index 2580ac8f..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 @@ -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/mobject/svg_mobject.py b/mobject/svg_mobject.py index 5c237a19..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): @@ -76,6 +79,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: @@ -83,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): @@ -122,6 +130,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): @@ -154,7 +171,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 +244,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": diff --git a/mobject/vectorized_mobject.py b/mobject/vectorized_mobject.py index f663367e..634b5f18 100644 --- a/mobject/vectorized_mobject.py +++ b/mobject/vectorized_mobject.py @@ -414,6 +414,9 @@ class VMobject(Mobject): b_residue = (num_cubics*b)%1 if b == 1: b_residue = 1 + elif lower_index == upper_index: + b_residue = (b_residue - a_residue)/(1-a_residue) + points[:4] = partial_bezier_points( points[:4], a_residue, 1 ) @@ -424,8 +427,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 = { diff --git a/scene/scene.py b/scene/scene.py index 8d992d93..9171a45d 100644 --- a/scene/scene.py +++ b/scene/scene.py @@ -29,6 +29,7 @@ class Scene(Container): "frame_duration" : LOW_QUALITY_FRAME_DURATION, "construct_args" : [], "skip_animations" : False, + "ignore_waits" : False, "write_to_movie" : False, "save_frames" : False, "save_pngs" : False, @@ -50,6 +51,7 @@ class Scene(Container): 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: @@ -466,6 +468,7 @@ class Scene(Container): 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: @@ -552,6 +555,10 @@ class Scene(Container): 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 7fc5d69b..e5e8a602 100644 --- a/topics/objects.py +++ b/topics/objects.py @@ -521,19 +521,76 @@ 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 + } +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)