diff --git a/manimlib/camera/camera.py b/manimlib/camera/camera.py index 03f618a5..ef2b60a3 100644 --- a/manimlib/camera/camera.py +++ b/manimlib/camera/camera.py @@ -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): diff --git a/manimlib/mobject/mobject.py b/manimlib/mobject/mobject.py index 077c7dc8..0f34ecad 100644 --- a/manimlib/mobject/mobject.py +++ b/manimlib/mobject/mobject.py @@ -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): diff --git a/manimlib/mobject/svg/svg_mobject.py b/manimlib/mobject/svg/svg_mobject.py index 974d988d..bdea884d 100644 --- a/manimlib/mobject/svg/svg_mobject.py +++ b/manimlib/mobject/svg/svg_mobject.py @@ -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: diff --git a/manimlib/mobject/types/vectorized_mobject.py b/manimlib/mobject/types/vectorized_mobject.py index 4e0dee7d..7481d3a4 100644 --- a/manimlib/mobject/types/vectorized_mobject.py +++ b/manimlib/mobject/types/vectorized_mobject.py @@ -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) diff --git a/old_projects/clacks/solution1.py b/old_projects/clacks/solution1.py index 25068ef4..682426c3 100644 --- a/old_projects/clacks/solution1.py +++ b/old_projects/clacks/solution1.py @@ -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) diff --git a/old_projects/domino_play.py b/old_projects/domino_play.py index e9f85ec6..c3aac42e 100644 --- a/old_projects/domino_play.py +++ b/old_projects/domino_play.py @@ -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(), diff --git a/old_projects/eoc/chapter5.py b/old_projects/eoc/chapter5.py index 0adcf241..aa3a4fc4 100644 --- a/old_projects/eoc/chapter5.py +++ b/old_projects/eoc/chapter5.py @@ -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) diff --git a/old_projects/patreon.py b/old_projects/patreon.py index f48f90e8..08e1f040 100644 --- a/old_projects/patreon.py +++ b/old_projects/patreon.py @@ -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) diff --git a/old_projects/wallis.py b/old_projects/wallis.py index 35c6ad38..7eb62ac6 100644 --- a/old_projects/wallis.py +++ b/old_projects/wallis.py @@ -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)