Better alignment of sub_mbojects

This commit is contained in:
Grant Sanderson
2016-04-14 19:30:47 -07:00
parent a9f620e250
commit bd3783586a
8 changed files with 92 additions and 64 deletions

View File

@ -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:

View File

@ -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

View File

@ -5,5 +5,5 @@ __all__ = [
]
from mobject import Mobject
from point_cloud_mobject import Point, Mobject1D, Mobject2D, PointCloudMobject
from vectorized_mobject import VectorizedMobject
from point_cloud_mobject import Point, Mobject1D, Mobject2D, PMobject
from vectorized_mobject import VMobject

View File

@ -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
"""

View File

@ -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

View File

@ -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])

View File

@ -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,

View File

@ -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 [