def insert_n_curves_to_point_list(self, n, points): nppc = self.n_points_per_curve if len(points) == 1: return np.repeat(points, nppc * n, 0) bezier_groups = self.get_bezier_tuples_from_points(points) norms = np.array( [get_norm(bg[nppc - 1] - bg[0]) for bg in bezier_groups]) total_norm = sum(norms) # Calculate insertions per curve (ipc) if total_norm < 1e-6: ipc = [n] + [0] * (len(bezier_groups) - 1) else: ipc = np.round(n * norms / sum(norms)).astype(int) diff = n - sum(ipc) for x in range(diff): ipc[np.argmin(ipc)] += 1 for x in range(-diff): ipc[np.argmax(ipc)] -= 1 new_points = [] for group, n_inserts in zip(bezier_groups, ipc): # What was once a single quadratic curve defined # by "group" will now be broken into n_inserts + 1 # smaller quadratic curves alphas = np.linspace(0, 1, n_inserts + 2) for a1, a2 in zip(alphas, alphas[1:]): new_points += partial_quadratic_bezier_points(group, a1, a2) return np.vstack(new_points)
def pointwise_become_partial(self, vmobject, a, b): assert isinstance(vmobject, VMobject) if a <= 0 and b >= 1: self.become(vmobject) return self num_curves = vmobject.get_num_curves() nppc = self.n_points_per_curve # Partial curve includes three portions: # - A middle section, which matches the curve exactly # - A start, which is some ending portion of an inner quadratic # - An end, which is the starting portion of a later inner quadratic lower_index, lower_residue = integer_interpolate(0, num_curves, a) upper_index, upper_residue = integer_interpolate(0, num_curves, b) i1 = nppc * lower_index i2 = nppc * (lower_index + 1) i3 = nppc * upper_index i4 = nppc * (upper_index + 1) vm_points = vmobject.get_points() new_points = vm_points.copy() if num_curves == 0: new_points[:] = 0 return self if lower_index == upper_index: tup = partial_quadratic_bezier_points( vm_points[i1:i2], lower_residue, upper_residue ) new_points[:i1] = tup[0] new_points[i1:i4] = tup new_points[i4:] = tup[2] new_points[nppc:] = new_points[nppc - 1] else: low_tup = partial_quadratic_bezier_points( vm_points[i1:i2], lower_residue, 1 ) high_tup = partial_quadratic_bezier_points( vm_points[i3:i4], 0, upper_residue ) new_points[0:i1] = low_tup[0] new_points[i1:i2] = low_tup # Keep new_points i2:i3 as they are new_points[i3:i4] = high_tup new_points[i4:] = high_tup[2] self.set_points(new_points) return self
def subdivide_sharp_curves(self, angle_threshold=30 * DEGREES, recurse=True): vmobs = [vm for vm in self.get_family(recurse) if vm.has_points()] for vmob in vmobs: new_points = [] for tup in vmob.get_bezier_tuples(): angle = angle_between_vectors(tup[1] - tup[0], tup[2] - tup[1]) if angle > angle_threshold: n = int(np.ceil(angle / angle_threshold)) alphas = np.linspace(0, 1, n + 1) new_points.extend([ partial_quadratic_bezier_points(tup, a1, a2) for a1, a2 in zip(alphas, alphas[1:]) ]) else: new_points.append(tup) vmob.set_points(np.vstack(new_points)) return self