def pointwise_become_partial(self, vmobject, a, b): assert(isinstance(vmobject, VMobject)) # Partial curve includes three portions: # - A middle section, which matches the curve exactly # - A start, which is some ending portion of an inner cubic # - An end, which is the starting portion of a later inner cubic if a <= 0 and b >= 1: self.set_points(vmobject.points) return self bezier_quads = vmobject.get_cubic_bezier_tuples() num_cubics = len(bezier_quads) lower_index, lower_residue = integer_interpolate(0, num_cubics, a) upper_index, upper_residue = integer_interpolate(0, num_cubics, b) self.clear_points() if num_cubics == 0: return self if lower_index == upper_index: self.append_points(partial_bezier_points( bezier_quads[lower_index], lower_residue, upper_residue )) else: self.append_points(partial_bezier_points( bezier_quads[lower_index], lower_residue, 1 )) for quad in bezier_quads[lower_index + 1:upper_index]: self.append_points(quad) self.append_points(partial_bezier_points( bezier_quads[upper_index], 0, upper_residue )) return self
def pointwise_become_partial(self, vmobject, a, b): assert (isinstance(vmobject, VMobject)) # Partial curve includes three portions: # - A middle section, which matches the curve exactly # - A start, which is some ending portion of an inner cubic # - An end, which is the starting portion of a later inner cubic if a <= 0 and b >= 1: self.set_points(vmobject.points) return self bezier_quads = vmobject.get_cubic_bezier_tuples() num_cubics = len(bezier_quads) lower_index, lower_residue = integer_interpolate(0, num_cubics, a) upper_index, upper_residue = integer_interpolate(0, num_cubics, b) self.clear_points() if num_cubics == 0: return self if lower_index == upper_index: self.append_points( partial_bezier_points(bezier_quads[lower_index], lower_residue, upper_residue)) else: self.append_points( partial_bezier_points(bezier_quads[lower_index], lower_residue, 1)) for quad in bezier_quads[lower_index + 1:upper_index]: self.append_points(quad) self.append_points( partial_bezier_points(bezier_quads[upper_index], 0, upper_residue)) return self
def pointwise_become_partial(self, mobject, a, b): assert(isinstance(mobject, VMobject)) # Partial curve includes three portions: # - A middle section, which matches the curve exactly # - A start, which is some ending portion of an inner cubic # - An end, which is the starting portion of a later inner cubic if a <= 0 and b >= 1: self.set_points(mobject.points) self.mark_paths_closed = mobject.mark_paths_closed return self self.mark_paths_closed = False num_cubics = mobject.get_num_anchor_points() - 1 lower_index = int(a * num_cubics) upper_index = int(b * num_cubics) points = np.array( mobject.points[3 * lower_index:3 * upper_index + 4] ) if len(points) > 1: a_residue = (num_cubics * a) % 1 b_residue = (num_cubics * b) % 1 if b == 1: b_residue = 1 elif lower_index == upper_index: b_residue = (b_residue - a_residue) / (1 - a_residue) points[:4] = partial_bezier_points( points[:4], a_residue, 1 ) points[-4:] = partial_bezier_points( points[-4:], 0, b_residue ) self.set_points(points) return self
def insert_n_curves_to_point_list(self, n, points): if len(points) == 1: nppcc = self.n_points_per_cubic_curve return np.repeat(points, nppcc * n, 0) 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 # it's total length is target_num. For example, # with curr_num = 10, target_num = 15, this would # be [0, 0, 1, 2, 2, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9] repeat_indices = (np.arange(target_num) * curr_num) // target_num # If the nth term of this list is k, it means # that the nth curve of our path should be split # into k pieces. In the above example, this would # be [2, 1, 2, 1, 2, 1, 2, 1, 2, 1] split_factors = [sum(repeat_indices == i) for i in range(curr_num)] 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 "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:]): new_points = np.append(new_points, partial_bezier_points(quad, a1, a2), axis=0) return new_points
def insert_n_anchor_points(self, n): curr = self.get_num_anchor_points() if curr == 0: self.points = np.zeros((1, 3)) n = n - 1 if curr == 1: self.points = np.repeat(self.points, 3 * n + 1, axis=0) return self points = np.array([self.points[0]]) num_curves = curr - 1 # Curves in self are buckets, and we need to know # how many new anchor points to put into each one. # Each element of index_allocation is like a bucket, # and its value tells you the appropriate index of # the smaller curve. index_allocation = ( np.arange(curr + n - 1) * num_curves) // (curr + n - 1) for index in range(num_curves): curr_bezier_points = self.points[3 * index:3 * index + 4] num_inter_curves = sum(index_allocation == index) alphas = np.linspace(0, 1, num_inter_curves + 1) # alphas = np.arange(0, num_inter_curves+1)/float(num_inter_curves) for a, b in zip(alphas, alphas[1:]): new_points = partial_bezier_points( curr_bezier_points, a, b ) points = np.append( points, new_points[1:], axis=0 ) self.set_points(points) return self
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_bezier_points(group, a1, a2) return np.vstack(new_points)
def insert_n_curves_to_point_list(self, n, points): if len(points) == 1: nppcc = self.n_points_per_cubic_curve return np.repeat(points, nppcc * n, 0) 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 # it's total length is target_num. For example, # with curr_num = 10, target_num = 15, this would # be [0, 0, 1, 2, 2, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9] repeat_indices = (np.arange(target_num) * curr_num) // target_num # If the nth term of this list is k, it means # that the nth curve of our path should be split # into k pieces. In the above example, this would # be [2, 1, 2, 1, 2, 1, 2, 1, 2, 1] split_factors = [ sum(repeat_indices == i) for i in range(curr_num) ] 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 "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:]): new_points = np.append( new_points, partial_bezier_points(quad, a1, a2), axis=0 ) return new_points
def pointwise_become_partial(self, vmobject, a, b): assert (isinstance(vmobject, VMobject)) assert (len(self.points) >= len(vmobject.points)) if a <= 0 and b >= 1: self.points[:] = vmobject.points return self bezier_tuple = vmobject.get_bezier_tuples() num_curves = len(bezier_tuple) # 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) new_point_list = [] if num_curves == 0: self.points[:] = 0 return self if lower_index == upper_index: new_point_list.append( partial_bezier_points(bezier_tuple[lower_index], lower_residue, upper_residue)) else: new_point_list.append( partial_bezier_points(bezier_tuple[lower_index], lower_residue, 1)) for tup in bezier_tuple[lower_index + 1:upper_index]: new_point_list.append(tup) new_point_list.append( partial_bezier_points(bezier_tuple[upper_index], 0, upper_residue)) new_points = np.vstack(new_point_list) self.points[:len(new_points)] = new_points self.points[len(new_points):] = new_points[-1] return self
def subdivide_sharp_curves(self, angle_threshold=30 * DEGREES, family=True): if family: vmobs = self.family_members_with_points() else: vmobs = [self] if self.has_points() else [] 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_bezier_points(tup, a1, a2) for a1, a2 in zip(alphas, alphas[1:]) ]) else: new_points.append(tup) vmob.points = np.vstack(new_points) return self