Remove add_control_points, re-implement VMobject.align_points

This commit is contained in:
Grant Sanderson 2019-02-05 13:12:55 -08:00
parent eaf25ff34b
commit 1f46904f9b
9 changed files with 88 additions and 53 deletions

View file

@ -365,7 +365,7 @@ class Camera(object):
)
last_p3 = None
quads = vmobject.get_all_cubic_bezier_point_tuples()
quads = vmobject.get_cubic_bezier_tuples()
ctx.new_path()
for p0, p1, p2, p3 in quads:
if should_start_new_path(last_p3, p0):

View file

@ -878,10 +878,10 @@ class Mobject(Container):
]).arrange(v2, **kwargs)
return self
def sort(self, point_to_num_func=lambda p: p[0]):
self.submobjects.sort(
key=lambda m: point_to_num_func(m.get_center())
)
def sort(self, point_to_num_func=lambda p: p[0], submob_func=None):
if submob_func is None:
submob_func = lambda m: point_to_num_func(m.get_center())
self.submobjects.sort(key=submob_func)
return self
def shuffle(self, recursive=False):

View file

@ -350,7 +350,6 @@ class VMobjectFromSVGPathstring(VMobject):
if command == "M": # moveto
self.start_new_path(new_points[0])
if len(new_points) <= 1:
return
@ -383,12 +382,7 @@ class VMobjectFromSVGPathstring(VMobject):
elif command == "A": # elliptical Arc
raise Exception("Not implemented")
elif command == "Z": # closepath
if is_closed(points):
return
# Both handles and new anchor are the start
# TODO, is this needed?
new_points = points[[0, 0, 0]]
# self.mark_paths_closed = True
return
# Handle situations where there's multiple relative control points
if isLower and len(new_points) > 3:

View file

@ -464,16 +464,6 @@ class VMobject(Mobject):
])
return self
# TODO, remove
# def add_control_points(self, control_points):
# assert(len(control_points) % 3 == 0)
# self.points = np.append(
# self.points,
# control_points,
# axis=0
# )
# return self
def has_new_path_started(self):
nppcc = self.n_points_per_cubic_curve # 4
return len(self.points) % nppcc == 1
@ -580,19 +570,37 @@ class VMobject(Mobject):
)
# Information about line
def get_all_cubic_bezier_point_tuples(self):
points = self.get_points()
if self.has_new_path_started():
points = points[:-1]
# TODO, Throw error if len(points) is > 1 and
# not divisible by 4 (i.e. n_points_per_cubic_curve)
nppcc = self.n_points_per_cubic_curve
def get_cubic_bezier_tuples_from_points(self, points):
nppcc = VMobject.CONFIG["n_points_per_cubic_curve"]
remainder = len(points) % nppcc
points = points[:len(points) - remainder]
return np.array([
points[i:i + nppcc]
for i in range(0, len(points), nppcc)
])
def get_cubic_bezier_tuples(self):
return self.get_cubic_bezier_tuples_from_points(
self.get_points()
)
def get_subpaths_from_points(self, points):
nppcc = VMobject.CONFIG["n_points_per_cubic_curve"]
split_indices = filter(
lambda n: not self.consider_points_equals(
points[n - 1], points[n]
),
range(nppcc, len(points), nppcc)
)
split_indices = [0] + list(split_indices) + [len(points)]
return [
points[i1:i2]
for i1, i2 in zip(split_indices, split_indices[1:])
]
def get_subpaths(self):
return self.get_subpaths_from_points(self.get_points())
def get_nth_curve_points(self, n):
assert(n < self.get_num_curves())
nppcc = self.n_points_per_cubic_curve
@ -645,9 +653,35 @@ class VMobject(Mobject):
# Alignment
def align_points(self, vmobject):
# This will call back to align_points_with_larger
Mobject.align_points(self, vmobject)
self.align_rgbas(vmobject)
nppcc = self.n_points_per_cubic_curve
subpaths1 = self.get_subpaths()
subpaths2 = vmobject.get_subpaths()
new_path1 = np.zeros((0, self.dim))
new_path2 = np.zeros((0, self.dim))
n_subpaths = max(len(subpaths1), len(subpaths2))
def get_nth_subpath(path_list, n):
if n < len(path_list):
return path_list[n]
last_path = path_list[n - 1]
return [last_path[-1]] * nppcc
for n in range(n_subpaths):
sp1 = get_nth_subpath(subpaths1, n)
sp2 = get_nth_subpath(subpaths2, n)
diff = len(sp2) - len(sp1)
if diff < 0:
sp2 = self.insert_n_curves_to_point_list(
-diff // nppcc, sp2
)
elif diff > 0:
sp1 = self.insert_n_curves_to_point_list(
diff // nppcc, sp1
)
new_path1 = np.append(new_path1, sp1, axis=0)
new_path2 = np.append(new_path2, sp2, axis=0)
self.set_points(new_path1)
vmobject.set_points(new_path2)
return self
def align_points_with_larger(self, larger_mobject):
@ -661,8 +695,18 @@ class VMobject(Mobject):
new_path_point = None
if self.has_new_path_started():
new_path_point = self.get_last_point()
curr_curve_points = self.get_all_cubic_bezier_point_tuples()
curr_num = len(curr_curve_points)
self.set_points(
self.insert_n_curves_to_point_list(
n, self.get_points()
)
)
if new_path_point is not None:
self.append_points([new_path_point])
return self
def insert_n_curves_to_point_list(self, n, points):
bezier_quads = self.get_cubic_bezier_tuples_from_points(points)
curr_num = len(bezier_quads)
target_num = curr_num + n
# This is an array with values ranging from 0
# up to curr_num, with repeats such that
@ -679,20 +723,19 @@ class VMobject(Mobject):
sum(repeat_indices == i)
for i in range(curr_num)
]
self.clear_points()
for points, sf in zip(curr_curve_points, split_factors):
new_points = np.zeros((0, self.dim))
for quad, sf in zip(bezier_quads, split_factors):
# What was once a single cubic curve defined
# by "points" will now be broken into sf
# by "quad" will now be broken into sf
# smaller cubic curves
alphas = np.linspace(0, 1, sf + 1)
for a1, a2 in zip(alphas, alphas[1:]):
self.append_points(
partial_bezier_points(points, a1, a2)
new_points = np.append(
new_points,
partial_bezier_points(quad, a1, a2),
axis=0
)
if new_path_point is not None:
self.append_points([new_path_point])
return self
return new_points
def align_rgbas(self, vmobject):
attrs = ["fill_rgbas", "stroke_rgbas", "background_stroke_rgbas"]
@ -740,7 +783,7 @@ class VMobject(Mobject):
if a <= 0 and b >= 1:
self.set_points(vmobject.points)
return self
bezier_quads = vmobject.get_all_cubic_bezier_point_tuples()
bezier_quads = vmobject.get_cubic_bezier_tuples()
num_cubics = len(bezier_quads)
lower_index, lower_residue = integer_interpolate(0, num_cubics, a)

View file

@ -1678,10 +1678,8 @@ class AnalyzeCircleGeometry(CircleDiagramFromSlidingBlocks, MovingCameraScene):
two_theta_labels.add(label)
wedge = arc.copy()
wedge.add_control_points([
*3 * [ORIGIN],
*3 * [wedge.points[0]]
])
wedge.add_line_to(ORIGIN)
wedge.add_line_to(wedge.points[0])
wedge.set_stroke(width=0)
wedge.set_fill(arc.get_color(), 0.2)
wedges.add(wedge)

View file

@ -842,7 +842,7 @@ class Test(Scene):
for arc, vect in zip(arcs, [DOWN+RIGHT, RIGHT]):
arc_copy = arc.copy()
point = domino1.get_critical_point(vect)
arc_copy.add_control_points(3*[point])
arc_copy.add_line_to([point])
arc_copy.set_stroke(width = 0)
arc_copy.set_fill(
arc.get_stroke_color(),

View file

@ -608,7 +608,7 @@ class FakeDiagram(TeacherStudentsScene):
axis_point = end_point[0]*RIGHT + gs.graph_origin[1]*UP
for alpha in np.linspace(0, 1, 20):
point = interpolate(axis_point, graph.points[0], alpha)
graph.add_control_points(3*[point])
graph.add_line_to(point)
graph.set_stroke(width = 1)
graph.set_fill(opacity = 1)
graph.set_color(BLUE_D)

View file

@ -574,7 +574,7 @@ class IntegrationByParts(Scene):
regions = []
for vect, color in (UP+RIGHT, BLUE), (DOWN+LEFT, GREEN):
region = curve.copy()
region.add_control_points(3*[rect.get_corner(vect)])
region.add_line_to(rect.get_corner(vect))
region.set_stroke(width = 0)
region.set_fill(color = color, opacity = 0.5)
regions.append(region)

View file

@ -340,7 +340,7 @@ class SourcesOfOriginality(TeacherStudentsScene):
blob1, blob2 = VMobject(), VMobject()
blob1.set_points_smoothly(points + [points[0]])
blob1.add_control_points(3 * len(added_points) * [points[0]])
blob1.append_points(3 * len(added_points) * [points[0]])
blob2.set_points_smoothly(points + added_points + [points[0]])
for blob in blob1, blob2:
blob.set_stroke(width=0)