From f64c076e7ebe70d489e081db43dcd5c5d722a0cb Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Wed, 17 Jan 2018 16:04:07 -0800 Subject: [PATCH] Changed how functions are applied to vectorized mobjects to handle smoothness issues more cleanly --- mobject/vectorized_mobject.py | 46 ++++++++++++++++++++++++++--------- topics/geometry.py | 5 ---- topics/number_line.py | 4 +-- topics/objects.py | 3 --- 4 files changed, 35 insertions(+), 23 deletions(-) diff --git a/mobject/vectorized_mobject.py b/mobject/vectorized_mobject.py index d6d53441..deed8d98 100644 --- a/mobject/vectorized_mobject.py +++ b/mobject/vectorized_mobject.py @@ -14,8 +14,9 @@ class VMobject(Mobject): "is_subpath" : False, "close_new_points" : False, "mark_paths_closed" : False, - "considered_smooth" : True, "propagate_style_to_family" : False, + "pre_function_handle_to_anchor_scale_factor" : 0.01, + "make_smooth_after_applying_functions" : False, } ## Colors @@ -191,7 +192,6 @@ class VMobject(Mobject): return self def make_smooth(self): - self.considered_smooth = True return self.change_anchor_mode("smooth") def make_jagged(self): @@ -232,12 +232,35 @@ class VMobject(Mobject): 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) - 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() 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 @@ -271,8 +294,7 @@ class VMobject(Mobject): return self.get_anchors() - ## Alignment - + ## Alignment def align_points(self, mobject): Mobject.align_points(self, mobject) 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 self.mark_paths_closed = mobject.mark_paths_closed = mark_closed return self - + def align_points_with_larger(self, larger_mobject): assert(isinstance(larger_mobject, VMobject)) self.insert_n_anchor_points( @@ -288,7 +310,7 @@ class VMobject(Mobject): self.get_num_anchor_points() ) return self - + def insert_n_anchor_points(self, n): curr = self.get_num_anchor_points() if curr == 0: @@ -318,17 +340,17 @@ class VMobject(Mobject): ) self.set_points(points) return self - + def get_point_mobject(self, center = None): if center is None: center = self.get_center() return VectorizedPoint(center) - + def repeat_submobject(self, submobject): if submobject.is_subpath: return VectorizedPoint(submobject.points[0]) return submobject.copy() - + def interpolate_color(self, mobject1, mobject2, alpha): attrs = [ "stroke_rgb", @@ -345,7 +367,7 @@ class VMobject(Mobject): if alpha == 1.0: # print getattr(mobject2, attr) setattr(self, attr, getattr(mobject2, attr)) - + def pointwise_become_partial(self, mobject, a, b): assert(isinstance(mobject, VMobject)) #Partial curve includes three portions: diff --git a/topics/geometry.py b/topics/geometry.py index 1883ad03..eadb6889 100644 --- a/topics/geometry.py +++ b/topics/geometry.py @@ -114,7 +114,6 @@ class Sector(VMobject): class Line(VMobject): CONFIG = { "buff" : 0, - "considered_smooth" : False, "path_arc" : 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) for alpha in np.linspace(0, 1, self.n_arc_anchors) ]) - self.considered_smooth = True self.account_for_buff() def account_for_buff(self): @@ -449,7 +447,6 @@ class Polygon(VMobject): "color" : GREEN_D, "mark_paths_closed" : True, "close_new_points" : True, - "considered_smooth" : False, } def __init__(self, *vertices, **kwargs): assert len(vertices) > 1 @@ -479,7 +476,6 @@ class Rectangle(VMobject): "width" : 4.0, "mark_paths_closed" : True, "close_new_points" : True, - "considered_smooth" : False, } def generate_points(self): y, x = self.height/2., self.width/2. @@ -586,7 +582,6 @@ class Grid(VMobject): CONFIG = { "height" : 6.0, "width" : 6.0, - "considered_smooth" : False, } def __init__(self, rows, columns, **kwargs): digest_config(self, kwargs, locals()) diff --git a/topics/number_line.py b/topics/number_line.py index 7fa6c40f..3b631b67 100644 --- a/topics/number_line.py +++ b/topics/number_line.py @@ -223,6 +223,7 @@ class NumberPlane(VMobject): "secondary_line_ratio" : 1, "written_coordinate_height" : 0.2, "propagate_style_to_family" : False, + "make_smooth_after_applying_functions" : True, } def generate_points(self): if self.x_radius is None: @@ -360,9 +361,6 @@ class NumberPlane(VMobject): mob.make_smooth() return self - def apply_function(self, function, maintain_smoothness = True): - VMobject.apply_function(self, function, maintain_smoothness = maintain_smoothness) - diff --git a/topics/objects.py b/topics/objects.py index bedc7b25..66434722 100644 --- a/topics/objects.py +++ b/topics/objects.py @@ -267,7 +267,6 @@ class VideoIcon(SVGMobject): CONFIG = { "file_name" : "video_icon", "width" : 2*SPACE_WIDTH/12., - "considered_smooth" : False, } def __init__(self, **kwargs): SVGMobject.__init__(self, **kwargs) @@ -275,8 +274,6 @@ class VideoIcon(SVGMobject): self.scale_to_fit_width(self.width) self.set_stroke(color = WHITE, width = 0) self.set_fill(color = WHITE, opacity = 1) - for mob in self: - mob.considered_smooth = False class VideoSeries(VGroup): CONFIG = {