diff --git a/animation/transform.py b/animation/transform.py index 12a8d98f..62adebc1 100644 --- a/animation/transform.py +++ b/animation/transform.py @@ -15,9 +15,9 @@ class Transform(Animation): "path_func" : straight_path } def __init__(self, mobject, ending_mobject, **kwargs): - mobject = instantiate(mobject) + mobject =mobject #Copy ending_mobject so as to not mess with caller - ending_mobject = instantiate(ending_mobject).copy() + ending_mobject = ending_mobject.copy() digest_config(self, kwargs, locals()) count1, count2 = mobject.get_num_points(), ending_mobject.get_num_points() if count2 == 0: diff --git a/camera.py b/camera.py index 47b54e4b..aff29807 100644 --- a/camera.py +++ b/camera.py @@ -9,7 +9,7 @@ import progressbar import aggdraw from helpers import * -from mobject import PointCloudMobject, VectorizedMobject +from mobject import PMobject, VMobject class Camera(object): CONFIG = { @@ -72,9 +72,9 @@ class Camera(object): for mob in mobjects ]) for mobject in mobjects: - if isinstance(mobject, VectorizedMobject): + if isinstance(mobject, VMobject): self.display_vectorized(mobject) - elif isinstance(mobject, PointCloudMobject): + elif isinstance(mobject, PMobject): self.display_point_cloud( mobject.points, mobject.rgbs, self.adjusted_thickness(mobject.stroke_width) @@ -95,34 +95,34 @@ class Camera(object): self.pixel_array[covered] = rgb - def display_vectorized(self, vect_mobject): - if vect_mobject.is_subpath: + def display_vectorized(self, vmobject): + if vmobject.is_subpath: #Subpath vectorized mobjects are taken care #of by their parent return im = Image.fromarray(self.pixel_array, mode = "RGB") canvas = aggdraw.Draw(im) - pen, fill = self.get_pen_and_fill(vect_mobject) - pathstring = self.get_pathstring(vect_mobject) + pen, fill = self.get_pen_and_fill(vmobject) + pathstring = self.get_pathstring(vmobject) symbol = aggdraw.Symbol(pathstring) canvas.symbol((0, 0), symbol, pen, fill) canvas.flush() self.pixel_array[:,:] = np.array(im) - def get_pen_and_fill(self, vect_mobject): + def get_pen_and_fill(self, vmobject): pen = aggdraw.Pen( - vect_mobject.get_stroke_color().get_hex_l(), - vect_mobject.stroke_width + vmobject.get_stroke_color().get_hex_l(), + vmobject.stroke_width ) fill = aggdraw.Brush( - vect_mobject.get_fill_color().get_hex_l(), - opacity = int(255*vect_mobject.get_fill_opacity()) + vmobject.get_fill_color().get_hex_l(), + opacity = int(255*vmobject.get_fill_opacity()) ) return (pen, fill) - def get_pathstring(self, vect_mobject): + def get_pathstring(self, vmobject): result = "" - for mob in [vect_mobject]+vect_mobject.subpath_mobjects: + for mob in [vmobject]+vmobject.subpath_mobjects: points = mob.points if len(points) == 0: continue diff --git a/mobject/__init__.py b/mobject/__init__.py index 98c80109..3ace2364 100644 --- a/mobject/__init__.py +++ b/mobject/__init__.py @@ -5,5 +5,5 @@ __all__ = [ ] from mobject import Mobject -from point_cloud_mobject import Point, Mobject1D, Mobject2D, PointCloudMobject -from vectorized_mobject import VectorizedMobject \ No newline at end of file +from point_cloud_mobject import Point, Mobject1D, Mobject2D, PMobject +from vectorized_mobject import VMobject \ No newline at end of file diff --git a/mobject/image_mobject.py b/mobject/image_mobject.py index 33252160..47353784 100644 --- a/mobject/image_mobject.py +++ b/mobject/image_mobject.py @@ -6,9 +6,9 @@ from random import random from helpers import * from mobject import Mobject -from point_cloud_mobject import PointCloudMobject +from point_cloud_mobject import PMobject -class ImageMobject(PointCloudMobject): +class ImageMobject(PMobject): """ Automatically filters out black pixels """ diff --git a/mobject/mobject.py b/mobject/mobject.py index 6bf3ead3..a5b2c0a3 100644 --- a/mobject/mobject.py +++ b/mobject/mobject.py @@ -17,11 +17,11 @@ class Mobject(object): """ #Number of numbers used to describe a point (3 for pos, 3 for normal vector) CONFIG = { - "color" : WHITE, + "color" : WHITE, "stroke_width" : DEFAULT_POINT_THICKNESS, - "name" : None, - "display_mode" : "points", #TODO, REMOVE - "dim" : 3, + "name" : None, + "dim" : 3, + "target" : None } def __init__(self, *sub_mobjects, **kwargs): digest_config(self, kwargs) @@ -320,11 +320,8 @@ class Mobject(object): ### Getters ### - def get_num_points(self, including_submobjects = False): - if including_submobjects: - return self.reduce_across_dimension(len, sum, 0) - else: - return len(self.points) + def get_num_points(self): + return len(self.points) def get_critical_point(self, direction): result = np.zeros(self.dim) @@ -406,23 +403,13 @@ class Mobject(object): ## Alignment def align_data(self, mobject): + self.align_sub_mobjects(mobject) self.align_points(mobject) #Recurse - diff = len(self.sub_mobjects) - len(mobject.sub_mobjects) - if diff != 0: - if diff < 0: - larger, smaller = mobject, self - elif diff > 0: - larger, smaller = self, mobject - for sub_mob in larger.sub_mobjects[-abs(diff):]: - point_mob = sub_mob.get_point_mobject( - smaller.get_center() - ) - smaller.add(point_mob) for m1, m2 in zip(self.sub_mobjects, mobject.sub_mobjects): m1.align_data(m2) - def get_point_mobject(self, center): + def get_point_mobject(self, center = None): """ The simplest mobject to be transformed to or from self. Should by a point of the appropriate type @@ -442,6 +429,40 @@ class Mobject(object): def align_points_with_larger(self, larger_mobject): raise Exception("Not implemented") + def align_sub_mobjects(self, mobject): + #If one is empty, and the other is not, + #push it into its submobject list + self_has_points, mob_has_points = [ + mob.get_num_points() > 0 + for mob in self, mobject + ] + if self_has_points and not mob_has_points: + self.push_self_into_sub_mobjects() + elif mob_has_points and not self_has_points: + mob.push_self_into_sub_mobjects() + self_count = len(self.sub_mobjects) + mob_count = len(mobject.sub_mobjects) + diff = abs(self_count-mob_count) + if self_count < mob_count: + self.add_n_more_sub_mobjects(diff) + elif mob_count < self_count: + mobject.add_n_more_sub_mobjects(diff) + return self + + def push_self_into_sub_mobjects(self): + copy = self.copy() + copy.sub_mobjects = [] + self.points = np.zeros((0, self.dim)) + self.add(copy) + return self + + def add_n_more_sub_mobjects(self, n): + if n > 0 and len(self.sub_mobjects) == 0: + self.add(self.copy()) + for i in range(n): + self.add(self.sub_mobjects[i].copy()) + return self + def interpolate(self, mobject1, mobject2, alpha, path_func): """ Turns target_mobject into an interpolation between mobject1 diff --git a/mobject/point_cloud_mobject.py b/mobject/point_cloud_mobject.py index b8730a00..a80fc4f6 100644 --- a/mobject/point_cloud_mobject.py +++ b/mobject/point_cloud_mobject.py @@ -1,7 +1,7 @@ from .mobject import Mobject from helpers import * -class PointCloudMobject(Mobject): +class PMobject(Mobject): def init_colors(self): self.rgbs = np.zeros((0, 3)) return self @@ -112,14 +112,14 @@ class PointCloudMobject(Mobject): # Alignment def align_points_with_larger(self, larger_mobject): - assert(isinstance(larger_mobject, PointCloudMobject)) + assert(isinstance(larger_mobject, PMobject)) self.apply_over_attr_arrays( lambda a : streth_array_to_length( a, larger_mobject.get_num_points() ) ) - def get_point_mobject(self, center): + def get_point_mobject(self, center = None): if center is None: center = self.get_center() return Point(center) @@ -141,7 +141,7 @@ class PointCloudMobject(Mobject): #TODO, Make the two implementations bellow non-redundant -class Mobject1D(PointCloudMobject): +class Mobject1D(PMobject): CONFIG = { "density" : DEFAULT_POINT_DENSITY_1D, } @@ -164,7 +164,7 @@ class Mobject1D(PointCloudMobject): ] self.add_points(points, color = color) -class Mobject2D(PointCloudMobject): +class Mobject2D(PMobject): CONFIG = { "density" : DEFAULT_POINT_DENSITY_2D, } @@ -175,11 +175,11 @@ class Mobject2D(PointCloudMobject): -class Point(PointCloudMobject): +class Point(PMobject): CONFIG = { "color" : BLACK, } def __init__(self, location = ORIGIN, **kwargs): - PointCloudMobject.__init__(self, **kwargs) + PMobject.__init__(self, **kwargs) self.add_points([location]) diff --git a/mobject/tex_mobject.py b/mobject/tex_mobject.py index ded6481c..2ec0f722 100644 --- a/mobject/tex_mobject.py +++ b/mobject/tex_mobject.py @@ -1,11 +1,11 @@ from mobject import Mobject -from point_cloud_mobject import PointCloudMobject +from point_cloud_mobject import PMobject from image_mobject import ImageMobject from helpers import * #TODO, Cleanup and refactor this file. -class TexMobject(PointCloudMobject): +class TexMobject(PMobject): CONFIG = { "template_tex_file" : TEMPLATE_TEX_FILE, "color" : WHITE, diff --git a/mobject/vectorized_mobject.py b/mobject/vectorized_mobject.py index c4f698d1..0db546c9 100644 --- a/mobject/vectorized_mobject.py +++ b/mobject/vectorized_mobject.py @@ -4,13 +4,14 @@ from .mobject import Mobject from helpers import * -class VectorizedMobject(Mobject): +class VMobject(Mobject): CONFIG = { "fill_color" : BLACK, "fill_opacity" : 0.0, #Indicates that it will not be displayed, but #that it should count in parent mobject's path "is_subpath" : False, + "closed" : True, } def __init__(self, *args, **kwargs): self.subpath_mobjects = [] @@ -18,8 +19,8 @@ class VectorizedMobject(Mobject): ## Colors def init_colors(self): - self.set_stroke(color = self.color) - self.set_fill(color = self.fill_color) + self.set_stroke(self.color, self.stroke_width) + self.set_fill(self.fill_color, self.fill_opacity) return self def set_family_attr(self, attr, value): @@ -102,7 +103,7 @@ class VectorizedMobject(Mobject): return self def set_points(self, points): - self.points = points + self.points = np.array(points) return self def set_anchor_points(self, points, mode = "smooth"): @@ -120,7 +121,7 @@ class VectorizedMobject(Mobject): def change_mode(self, mode): anchors, h1, h2 = self.get_anchors_and_handles() - self.set_points(anchors, mode = mode) + self.set_anchor_points(anchors, mode = mode) return self def make_smooth(self): @@ -131,7 +132,7 @@ class VectorizedMobject(Mobject): def add_subpath(self, points): """ - A VectorizedMobject is meant to represnt + A VMobject is meant to represnt a single "path", in the svg sense of the word. However, one such path may really consit of separate continuous components if there is a move_to command. @@ -140,7 +141,7 @@ class VectorizedMobject(Mobject): but will be tracked in a separate special list for when it comes time to display. """ - subpath_mobject = VectorizedMobject( + subpath_mobject = VMobject( is_subpath = True ) subpath_mobject.set_points(points) @@ -176,7 +177,13 @@ class VectorizedMobject(Mobject): ## Alignment def align_points_with_larger(self, larger_mobject): - assert(isinstance(larger_mobject, VectorizedMobject)) + assert(isinstance(larger_mobject, VMobject)) + num_anchors = self.get_num_anchor_points() + if num_anchors <= 1: + point = self.points[0] if len(self.points) else np.zeros(3) + self.points = np.zeros(larger_mobject.points.shape) + self.points[:,:] = point + return self points = np.array([self.points[0]]) target_len = larger_mobject.get_num_anchor_points()-1 num_curves = self.get_num_anchor_points()-1 @@ -199,7 +206,7 @@ class VectorizedMobject(Mobject): self.set_points(points) return self - def get_point_mobject(self, center): + def get_point_mobject(self, center = None): if center is None: center = self.get_center() return VectorizedPoint(center) @@ -219,7 +226,7 @@ class VectorizedMobject(Mobject): )) def become_partial(self, mobject, a, b): - assert(isinstance(mobject, VectorizedMobject)) + assert(isinstance(mobject, VMobject)) #Partial curve includes three portions: #-A middle section, which matches the curve exactly #-A start, which is some ending portion of an inner cubic @@ -246,18 +253,18 @@ class VectorizedMobject(Mobject): return self -class VectorizedPoint(VectorizedMobject): +class VectorizedPoint(VMobject): CONFIG = { "color" : BLACK, } def __init__(self, location = ORIGIN, **kwargs): - VectorizedMobject.__init__(self, **kwargs) + VMobject.__init__(self, **kwargs) self.set_points([location]) -class VectorizedMobjectFromSVGPathstring(VectorizedMobject): +class VMobjectFromSVGPathstring(VMobject): def __init__(self, path_string, **kwargs): digest_locals(self) - VectorizedMobject.__init__(self, **kwargs) + VMobject.__init__(self, **kwargs) def get_path_commands(self): return [