This commit is contained in:
Sridhar Ramesh 2018-01-17 23:17:53 -08:00
commit 24a39c7e03
11 changed files with 124 additions and 103 deletions

View file

@ -84,15 +84,38 @@ class Camera(object):
def reset(self): def reset(self):
self.set_pixel_array(np.array(self.background)) self.set_pixel_array(np.array(self.background))
####
def extract_mobject_family_members(self, mobjects, only_those_with_points = False):
if only_those_with_points:
method = Mobject.family_members_with_points
else:
method = Mobject.submobject_family
return remove_list_redundancies(list(
it.chain(*[
method(m)
for m in mobjects
if not (isinstance(m, VMobject) and m.is_subpath)
])
))
def capture_mobject(self, mobject): def capture_mobject(self, mobject):
return self.capture_mobjects([mobject]) return self.capture_mobjects([mobject])
def capture_mobjects(self, mobjects, include_submobjects = True): def capture_mobjects(
self, mobjects,
include_submobjects = True,
excluded_mobjects = None,
):
if include_submobjects: if include_submobjects:
mobjects = it.chain(*[ mobjects = self.extract_mobject_family_members(
mob.family_members_with_points() mobjects, only_those_with_points = True
for mob in mobjects )
]) if excluded_mobjects:
all_excluded = self.extract_mobject_family_members(
excluded_mobjects
)
mobjects = list_difference_update(mobjects, all_excluded)
vmobjects = [] vmobjects = []
for mobject in mobjects: for mobject in mobjects:
if isinstance(mobject, VMobject): if isinstance(mobject, VMobject):

View file

@ -43,7 +43,7 @@ DEFAULT_MOBJECT_TO_MOBJECT_BUFFER = MED_SMALL_BUFF
#All in seconds #All in seconds
DEFAULT_ANIMATION_RUN_TIME = 1.0 DEFAULT_ANIMATION_RUN_TIME = 1.0
DEFAULT_POINTWISE_FUNCTION_RUN_TIME = 3.0 DEFAULT_POINTWISE_FUNCTION_RUN_TIME = 3.0
DEFAULT_wait_TIME = 1.0 DEFAULT_WAIT_TIME = 1.0
ORIGIN = np.array(( 0., 0., 0.)) ORIGIN = np.array(( 0., 0., 0.))

View file

@ -454,7 +454,6 @@ class Mobject(object):
return self return self
def submobject_radial_gradient_highlight(self, center = None, radius = 1, inner_color = WHITE, outer_color = BLACK): def submobject_radial_gradient_highlight(self, center = None, radius = 1, inner_color = WHITE, outer_color = BLACK):
mobs = self.family_members_with_points() mobs = self.family_members_with_points()
if center == None: if center == None:
center = self.get_center() center = self.get_center()
@ -462,14 +461,11 @@ class Mobject(object):
for mob in self.family_members_with_points(): for mob in self.family_members_with_points():
t = np.linalg.norm(mob.get_center() - center)/radius t = np.linalg.norm(mob.get_center() - center)/radius
t = min(t,1) t = min(t,1)
print t
mob_color = interpolate_color(inner_color, outer_color, t) mob_color = interpolate_color(inner_color, outer_color, t)
print mob_color
mob.highlight(mob_color, family = False) mob.highlight(mob_color, family = False)
return self return self
def set_color(self, color): def set_color(self, color):
self.highlight(color) self.highlight(color)
self.color = Color(color) self.color = Color(color)

View file

@ -14,8 +14,9 @@ class VMobject(Mobject):
"is_subpath" : False, "is_subpath" : False,
"close_new_points" : False, "close_new_points" : False,
"mark_paths_closed" : False, "mark_paths_closed" : False,
"considered_smooth" : True,
"propagate_style_to_family" : False, "propagate_style_to_family" : False,
"pre_function_handle_to_anchor_scale_factor" : 0.01,
"make_smooth_after_applying_functions" : False,
} }
## Colors ## Colors
@ -191,7 +192,6 @@ class VMobject(Mobject):
return self return self
def make_smooth(self): def make_smooth(self):
self.considered_smooth = True
return self.change_anchor_mode("smooth") return self.change_anchor_mode("smooth")
def make_jagged(self): def make_jagged(self):
@ -232,12 +232,35 @@ class VMobject(Mobject):
self.submobjects self.submobjects
) )
def apply_function(self, function, maintain_smoothness = False): def apply_function(self, function):
factor = self.pre_function_handle_to_anchor_scale_factor
self.scale_handle_to_anchor_distances(factor)
Mobject.apply_function(self, function) Mobject.apply_function(self, function)
if maintain_smoothness and self.considered_smooth: self.scale_handle_to_anchor_distances(1./factor)
if self.make_smooth_after_applying_functions:
self.make_smooth() self.make_smooth()
return self return self
def scale_handle_to_anchor_distances(self, factor):
"""
If the distance between a given handle point H and its associated
anchor point A is d, then it changes H to be a distances factor*d
away from A, but so that the line from A to H doesn't change.
This is mostly useful in the context of applying a (differentiable)
function, to preserve tangency properties. One would pull all the
handles closer to their anchors, apply the function then push them out
again.
"""
if self.get_num_points() == 0:
return
anchors, handles1, handles2 = self.get_anchors_and_handles()
# print len(anchors), len(handles1), len(handles2)
a_to_h1 = handles1 - anchors[:-1]
a_to_h2 = handles2 - anchors[1:]
handles1 = anchors[:-1] + factor*a_to_h1
handles2 = anchors[1:] + factor*a_to_h2
self.set_anchors_and_handles(anchors, handles1, handles2)
## Information about line ## Information about line
@ -271,8 +294,7 @@ class VMobject(Mobject):
return self.get_anchors() return self.get_anchors()
## Alignment ## Alignment
def align_points(self, mobject): def align_points(self, mobject):
Mobject.align_points(self, mobject) Mobject.align_points(self, mobject)
is_subpath = self.is_subpath or mobject.is_subpath is_subpath = self.is_subpath or mobject.is_subpath
@ -280,7 +302,7 @@ class VMobject(Mobject):
mark_closed = self.mark_paths_closed and mobject.mark_paths_closed mark_closed = self.mark_paths_closed and mobject.mark_paths_closed
self.mark_paths_closed = mobject.mark_paths_closed = mark_closed self.mark_paths_closed = mobject.mark_paths_closed = mark_closed
return self return self
def align_points_with_larger(self, larger_mobject): def align_points_with_larger(self, larger_mobject):
assert(isinstance(larger_mobject, VMobject)) assert(isinstance(larger_mobject, VMobject))
self.insert_n_anchor_points( self.insert_n_anchor_points(
@ -288,7 +310,7 @@ class VMobject(Mobject):
self.get_num_anchor_points() self.get_num_anchor_points()
) )
return self return self
def insert_n_anchor_points(self, n): def insert_n_anchor_points(self, n):
curr = self.get_num_anchor_points() curr = self.get_num_anchor_points()
if curr == 0: if curr == 0:
@ -318,17 +340,17 @@ class VMobject(Mobject):
) )
self.set_points(points) self.set_points(points)
return self return self
def get_point_mobject(self, center = None): def get_point_mobject(self, center = None):
if center is None: if center is None:
center = self.get_center() center = self.get_center()
return VectorizedPoint(center) return VectorizedPoint(center)
def repeat_submobject(self, submobject): def repeat_submobject(self, submobject):
if submobject.is_subpath: if submobject.is_subpath:
return VectorizedPoint(submobject.points[0]) return VectorizedPoint(submobject.points[0])
return submobject.copy() return submobject.copy()
def interpolate_color(self, mobject1, mobject2, alpha): def interpolate_color(self, mobject1, mobject2, alpha):
attrs = [ attrs = [
"stroke_rgb", "stroke_rgb",
@ -345,7 +367,7 @@ class VMobject(Mobject):
if alpha == 1.0: if alpha == 1.0:
# print getattr(mobject2, attr) # print getattr(mobject2, attr)
setattr(self, attr, getattr(mobject2, attr)) setattr(self, attr, getattr(mobject2, attr))
def pointwise_become_partial(self, mobject, a, b): def pointwise_become_partial(self, mobject, a, b):
assert(isinstance(mobject, VMobject)) assert(isinstance(mobject, VMobject))
#Partial curve includes three portions: #Partial curve includes three portions:

View file

@ -123,19 +123,23 @@ class Scene(object):
def capture_mobjects_in_camera(self, mobjects, **kwargs): def capture_mobjects_in_camera(self, mobjects, **kwargs):
self.camera.capture_mobjects(mobjects, **kwargs) self.camera.capture_mobjects(mobjects, **kwargs)
def update_frame(self, mobjects = None, background = None, **kwargs): def update_frame(
if "include_submobjects" not in kwargs: self,
kwargs["include_submobjects"] = False mobjects = None,
background = None,
include_submobjects = True,
**kwargs):
if mobjects is None: if mobjects is None:
mobjects = list_update( mobjects = list_update(
self.foreground_mobjects,
self.mobjects, self.mobjects,
self.foreground_mobjects,
) )
if background is not None: if background is not None:
self.set_camera_pixel_array(background) self.set_camera_pixel_array(background)
else: else:
self.reset_camera() self.reset_camera()
kwargs["include_submobjects"] = include_submobjects
self.capture_mobjects_in_camera(mobjects, **kwargs) self.capture_mobjects_in_camera(mobjects, **kwargs)
def freeze_background(self): def freeze_background(self):
@ -167,15 +171,6 @@ class Scene(object):
### ###
def extract_mobject_family_members(self, *mobjects):
return remove_list_redundancies(list(
it.chain(*[
m.submobject_family()
for m in mobjects
if not (isinstance(m, VMobject) and m.is_subpath)
])
))
def get_top_level_mobjects(self): def get_top_level_mobjects(self):
# Return only those which are not in the family # Return only those which are not in the family
# of another mobject from the scene # of another mobject from the scene
@ -209,14 +204,10 @@ class Scene(object):
""" """
Mobjects will be displayed, from background to foreground, Mobjects will be displayed, from background to foreground,
in the order with which they are entered. in the order with which they are entered.
Scene class keeps track not just of the mobject directly added,
but also of every family member therein.
""" """
mobjects, continual_animations = self.separate_mobjects_and_continual_animations( mobjects, continual_animations = self.separate_mobjects_and_continual_animations(
mobjects_or_continual_animations mobjects_or_continual_animations
) )
mobjects = self.extract_mobject_family_members(*mobjects)
self.mobjects = list_update(self.mobjects, mobjects) self.mobjects = list_update(self.mobjects, mobjects)
self.continual_animations = list_update( self.continual_animations = list_update(
self.continual_animations, continual_animations self.continual_animations, continual_animations
@ -236,31 +227,38 @@ class Scene(object):
mobjects, continual_animations = self.separate_mobjects_and_continual_animations( mobjects, continual_animations = self.separate_mobjects_and_continual_animations(
mobjects_or_continual_animations mobjects_or_continual_animations
) )
mobjects = self.extract_mobject_family_members(*mobjects) to_remove = self.camera.extract_mobject_family_members(mobjects)
self.mobjects = filter(
lambda m : m not in mobjects,
self.mobjects
)
self.remove_mobjects_not_completely_on_screen()
self.remove_foreground_mobjects(*mobjects)
self.mobjects = self.get_restructured_mobject_list(self.mobjects, to_remove)
self.foreground_mobjects = self.get_restructured_mobject_list(
self.foreground_mobjects, to_remove
)
self.continual_animations = filter( self.continual_animations = filter(
lambda ca : ca not in continual_animations and \ lambda ca : ca not in continual_animations and \
ca.mobject not in mobjects, ca.mobject not in to_remove,
self.continual_animations self.continual_animations
) )
return self return self
def remove_mobjects_not_completely_on_screen(self): def get_restructured_mobject_list(self, mobjects, to_remove):
def should_keep(mobject): """
return all([ In cases where the scene contains a group, e.g. Group(m1, m2, m3), but one
submob in self.mobjects of its submobjects is removed, e.g. scene.remove(m1), the list of mobjects
for submob in mobject.family_members_with_points() will be editing to contain other submobjects, but not m1, e.g. it will now
]) insert m2 and m3 to where the group once was.
"""
self.mobjects = filter(should_keep, self.mobjects) new_mobjects = []
return self def add_safe_mobjects_from_list(list_to_examine, set_to_remove):
for mob in list_to_examine:
if mob in set_to_remove:
continue
intersect = set_to_remove.intersection(mob.submobject_family())
if intersect:
add_safe_mobjects_from_list(mob.submobjects, intersect)
else:
new_mobjects.append(mob)
add_safe_mobjects_from_list(mobjects, set(to_remove))
return new_mobjects
def add_foreground_mobjects(self, *mobjects): def add_foreground_mobjects(self, *mobjects):
self.foreground_mobjects = list_update( self.foreground_mobjects = list_update(
@ -274,9 +272,9 @@ class Scene(object):
return self.add_foreground_mobjects(mobject) return self.add_foreground_mobjects(mobject)
def remove_foreground_mobjects(self, *mobjects): def remove_foreground_mobjects(self, *mobjects):
self.foreground_mobjects = filter( self.foreground_mobjects = get_restructured_mobject_list(
lambda m : m not in mobjects, self.foreground_mobjects,
self.foreground_mobjects self.camera.extract_mobject_family_members(mobjects)
) )
return self return self
@ -289,7 +287,7 @@ class Scene(object):
def bring_to_back(self, *mobjects): def bring_to_back(self, *mobjects):
self.remove(*mobjects) self.remove(*mobjects)
self.mobjects = mobjects + self.mobjects self.mobjects = list(mobjects) + self.mobjects
return self return self
def clear(self): def clear(self):
@ -304,17 +302,13 @@ class Scene(object):
def get_mobject_copies(self): def get_mobject_copies(self):
return [m.copy() for m in self.mobjects] return [m.copy() for m in self.mobjects]
def separate_moving_and_static_mobjects(self, *animations): def get_moving_mobjects(self, *animations):
moving_mobjects = self.extract_mobject_family_members(*it.chain( moving_mobjects = list(it.chain(
[anim.mobject for anim in animations], [anim.mobject for anim in animations],
[ca.mobject for ca in self.continual_animations], [ca.mobject for ca in self.continual_animations],
self.foreground_mobjects, self.foreground_mobjects,
)) ))
static_mobjects = filter( return moving_mobjects
lambda m : m not in moving_mobjects,
self.mobjects
)
return moving_mobjects, static_mobjects
def get_time_progression(self, run_time): def get_time_progression(self, run_time):
times = np.arange(0, run_time, self.frame_duration) times = np.arange(0, run_time, self.frame_duration)
@ -395,9 +389,8 @@ class Scene(object):
self.num_plays += 1 self.num_plays += 1
sync_animation_run_times_and_rate_funcs(*animations, **kwargs) sync_animation_run_times_and_rate_funcs(*animations, **kwargs)
moving_mobjects, static_mobjects = \ moving_mobjects = self.get_moving_mobjects(*animations)
self.separate_moving_and_static_mobjects(*animations) self.update_frame(excluded_mobjects = moving_mobjects)
self.update_frame(static_mobjects)
static_image = self.get_frame() static_image = self.get_frame()
for t in self.get_animation_time_progression(animations): for t in self.get_animation_time_progression(animations):
for animation in animations: for animation in animations:
@ -422,7 +415,7 @@ class Scene(object):
return self.mobjects_from_last_animation return self.mobjects_from_last_animation
return [] return []
def wait(self, duration = DEFAULT_wait_TIME): def wait(self, duration = DEFAULT_WAIT_TIME):
if self.skip_animations: if self.skip_animations:
return self return self
@ -527,3 +520,7 @@ class Scene(object):
shutil.move(*self.args_to_rename_file) shutil.move(*self.args_to_rename_file)
else: else:
os.rename(*self.args_to_rename_file) os.rename(*self.args_to_rename_file)

View file

@ -122,15 +122,13 @@ class ZoomedScene(Scene):
self.zoomed_camera.capture_mobjects( self.zoomed_camera.capture_mobjects(
mobjects, **kwargs mobjects, **kwargs
) )
def separate_moving_and_static_mobjects(self, *animations): def get_moving_mobjects(self, *animations):
moving_mobjects, static_mobjects = Scene.separate_moving_and_static_mobjects( moving_mobjects = Scene.get_moving_mobjects(self, *animations)
self, *animations
)
if self.zoom_activated and self.little_rectangle in moving_mobjects: if self.zoom_activated and self.little_rectangle in moving_mobjects:
# When the camera is moving, so is everything, # When the camera is moving, so is everything,
return self.get_mobjects(), [] return self.mobjects
else: else:
return moving_mobjects, static_mobjects return moving_mobjects

View file

@ -114,7 +114,6 @@ class Sector(VMobject):
class Line(VMobject): class Line(VMobject):
CONFIG = { CONFIG = {
"buff" : 0, "buff" : 0,
"considered_smooth" : False,
"path_arc" : None, "path_arc" : None,
"n_arc_anchors" : 10, #Only used if path_arc is not None "n_arc_anchors" : 10, #Only used if path_arc is not None
} }
@ -132,7 +131,6 @@ class Line(VMobject):
path_func(self.start, self.end, alpha) path_func(self.start, self.end, alpha)
for alpha in np.linspace(0, 1, self.n_arc_anchors) for alpha in np.linspace(0, 1, self.n_arc_anchors)
]) ])
self.considered_smooth = True
self.account_for_buff() self.account_for_buff()
def account_for_buff(self): def account_for_buff(self):
@ -449,7 +447,6 @@ class Polygon(VMobject):
"color" : GREEN_D, "color" : GREEN_D,
"mark_paths_closed" : True, "mark_paths_closed" : True,
"close_new_points" : True, "close_new_points" : True,
"considered_smooth" : False,
} }
def __init__(self, *vertices, **kwargs): def __init__(self, *vertices, **kwargs):
assert len(vertices) > 1 assert len(vertices) > 1
@ -479,7 +476,6 @@ class Rectangle(VMobject):
"width" : 4.0, "width" : 4.0,
"mark_paths_closed" : True, "mark_paths_closed" : True,
"close_new_points" : True, "close_new_points" : True,
"considered_smooth" : False,
} }
def generate_points(self): def generate_points(self):
y, x = self.height/2., self.width/2. y, x = self.height/2., self.width/2.
@ -586,7 +582,6 @@ class Grid(VMobject):
CONFIG = { CONFIG = {
"height" : 6.0, "height" : 6.0,
"width" : 6.0, "width" : 6.0,
"considered_smooth" : False,
} }
def __init__(self, rows, columns, **kwargs): def __init__(self, rows, columns, **kwargs):
digest_config(self, kwargs, locals()) digest_config(self, kwargs, locals())

View file

@ -223,6 +223,7 @@ class NumberPlane(VMobject):
"secondary_line_ratio" : 1, "secondary_line_ratio" : 1,
"written_coordinate_height" : 0.2, "written_coordinate_height" : 0.2,
"propagate_style_to_family" : False, "propagate_style_to_family" : False,
"make_smooth_after_applying_functions" : True,
} }
def generate_points(self): def generate_points(self):
if self.x_radius is None: if self.x_radius is None:
@ -360,9 +361,6 @@ class NumberPlane(VMobject):
mob.make_smooth() mob.make_smooth()
return self return self
def apply_function(self, function, maintain_smoothness = True):
VMobject.apply_function(self, function, maintain_smoothness = maintain_smoothness)

View file

@ -67,8 +67,9 @@ class ChangingDecimal(Animation):
for x in range(self.spare_parts)] for x in range(self.spare_parts)]
) )
if self.tracked_mobject: if self.tracked_mobject:
self.diff_from_tracked_mobject = \ dmc = decimal_number_mobject.get_center()
decimal_number_mobject.get_center() - self.tracked_mobject.get_center() tmc = self.tracked_mobject.get_center()
self.diff_from_tracked_mobject = dmc - tmc
Animation.__init__(self, decimal_number_mobject, **kwargs) Animation.__init__(self, decimal_number_mobject, **kwargs)
def update_mobject(self, alpha): def update_mobject(self, alpha):
@ -85,14 +86,9 @@ class ChangingDecimal(Animation):
) )
new_decimal.replace(decimal, dim_to_match = 1) new_decimal.replace(decimal, dim_to_match = 1)
new_decimal.highlight(decimal.get_color()) new_decimal.highlight(decimal.get_color())
decimal.align_data(new_decimal)
families = [ decimal.submobjects = new_decimal.submobjects
mob.family_members_with_points() decimal.number = new_number
for mob in decimal, new_decimal
]
for sm1, sm2 in zip(*families):
sm1.interpolate(sm1, sm2, 1)
self.mobject.number = new_number
def update_position(self): def update_position(self):
if self.position_update_func is not None: if self.position_update_func is not None:

View file

@ -267,7 +267,6 @@ class VideoIcon(SVGMobject):
CONFIG = { CONFIG = {
"file_name" : "video_icon", "file_name" : "video_icon",
"width" : 2*SPACE_WIDTH/12., "width" : 2*SPACE_WIDTH/12.,
"considered_smooth" : False,
} }
def __init__(self, **kwargs): def __init__(self, **kwargs):
SVGMobject.__init__(self, **kwargs) SVGMobject.__init__(self, **kwargs)
@ -275,8 +274,6 @@ class VideoIcon(SVGMobject):
self.scale_to_fit_width(self.width) self.scale_to_fit_width(self.width)
self.set_stroke(color = WHITE, width = 0) self.set_stroke(color = WHITE, width = 0)
self.set_fill(color = WHITE, opacity = 1) self.set_fill(color = WHITE, opacity = 1)
for mob in self:
mob.considered_smooth = False
class VideoSeries(VGroup): class VideoSeries(VGroup):
CONFIG = { CONFIG = {

View file

@ -175,11 +175,10 @@ class ThreeDScene(Scene):
if is_camera_rotating: if is_camera_rotating:
self.add(self.ambient_camera_rotation) self.add(self.ambient_camera_rotation)
def separate_moving_and_static_mobjects(self, *animations): def get_moving_mobjects(self, *animations):
moving, static = Scene.separate_moving_and_static_mobjects(self, *animations)
if self.camera.rotation_mobject in moving: if self.camera.rotation_mobject in moving:
return moving + static, [] return self.mobjects
return moving, static return Scene.get_moving_mobjects(self, *animations)
############## ##############