def round_corners(self, radius=0.5): vertices = self.get_vertices() arcs = [] for v1, v2, v3 in adjacent_n_tuples(vertices, 3): vect1 = v2 - v1 vect2 = v3 - v2 unit_vect1 = normalize(vect1) unit_vect2 = normalize(vect2) angle = angle_between_vectors(vect1, vect2) # Negative radius gives concave curves angle *= np.sign(radius) # Distance between vertex and start of the arc cut_off_length = radius * np.tan(angle / 2) # Determines counterclockwise vs. clockwise sign = np.sign(np.cross(vect1, vect2)[2]) arc = ArcBetweenPoints(v2 - unit_vect1 * cut_off_length, v2 + unit_vect2 * cut_off_length, angle=sign * angle) arcs.append(arc) self.clear_points() # To ensure that we loop through starting with last arcs = [arcs[-1], *arcs[:-1]] for arc1, arc2 in adjacent_pairs(arcs): self.append_points(arc1.points) line = Line(arc1.get_end(), arc2.get_start()) # Make sure anchors are evenly distributed len_ratio = line.get_length() / arc1.get_arc_length() line.insert_n_curves(int(arc1.get_num_curves() * len_ratio)) self.append_points(line.get_points()) return self
def round_corners(self, radius=0.5): vertices = self.get_vertices() arcs = [] for v1, v2, v3 in adjacent_n_tuples(vertices, 3): vect1 = v2 - v1 vect2 = v3 - v2 unit_vect1 = normalize(vect1) unit_vect2 = normalize(vect2) angle = angle_between_vectors(vect1, vect2) # Negative radius gives concave curves angle *= np.sign(radius) # Distance between vertex and start of the arc cut_off_length = radius * np.tan(angle / 2) # Determines counterclockwise vs. clockwise sign = np.sign(np.cross(vect1, vect2)[2]) arc = ArcBetweenPoints( v2 - unit_vect1 * cut_off_length, v2 + unit_vect2 * cut_off_length, angle=sign * angle ) arcs.append(arc) self.clear_points() # To ensure that we loop through starting with last arcs = [arcs[-1], *arcs[:-1]] for arc1, arc2 in adjacent_pairs(arcs): self.append_points(arc1.points) line = Line(arc1.get_end(), arc2.get_start()) # Make sure anchors are evenly distributed len_ratio = line.get_length() / arc1.get_arc_length() line.insert_n_curves( int(arc1.get_num_curves() * len_ratio) ) self.append_points(line.get_points()) return self
def round_corners(self, radius=0.5): vertices = self.get_vertices() arcs = [] for v1, v2, v3 in adjacent_n_tuples(vertices, 3): vect1 = v2 - v1 vect2 = v3 - v2 unit_vect1 = normalize(vect1) unit_vect2 = normalize(vect2) angle = angle_between_vectors(vect1, vect2) # Negative radius gives concave curves angle *= np.sign(radius) # Distance between vertex and start of the arc cut_off_length = radius * np.tan(angle / 2) # Determines counterclockwise vs. clockwise sign = np.sign(np.cross(vect1, vect2)[2]) arcs.append( ArcBetweenPoints(v2 - unit_vect1 * cut_off_length, v2 + unit_vect2 * cut_off_length, angle=sign * angle)) self.clear_points() # To ensure that we loop through starting with last arcs = [arcs[-1], *arcs[:-1]] for arc1, arc2 in adjacent_pairs(arcs): self.append_points(arc1.points) self.add_line_to(arc2.get_start()) return self
def viewing_angle_of_point(self, point): # as measured from the positive x-axis v1 = self.project(RIGHT) v2 = self.project(np.array(point) - self.get_source_point()) absolute_angle = angle_between_vectors(v1, v2) # determine the angle's sign depending on their plane's # choice of orientation. That choice is set by the camera # position, i. e. projection direction if np.dot(self.projection_direction(), np.cross(v1, v2)) > 0: return absolute_angle else: return -absolute_angle
def viewing_angle_of_point(self, point): # as measured from the positive x-axis v1 = self.project(RIGHT) v2 = self.project(np.array(point) - self.get_source_point()) absolute_angle = angle_between_vectors(v1, v2) # determine the angle's sign depending on their plane's # choice of orientation. That choice is set by the camera # position, i. e. projection direction if np.dot(self.projection_direction(), np.cross(v1, v2)) > 0: return absolute_angle else: return -absolute_angle
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
def new_sector(self, r, dr, lower_angle, upper_angle): alpha = self.max_opacity * self.opacity_function(r) annular_sector = AnnularSector(inner_radius=r, outer_radius=r + dr, color=self.color, fill_opacity=alpha, start_angle=lower_angle, angle=upper_angle - lower_angle) # rotate (not project) it into the viewing plane rotation_matrix = z_to_vector(self.projection_direction()) annular_sector.apply_matrix(rotation_matrix) # now rotate it inside that plane rotated_RIGHT = np.dot(RIGHT, rotation_matrix.T) projected_RIGHT = self.project(RIGHT) omega = angle_between_vectors(rotated_RIGHT, projected_RIGHT) annular_sector.rotate(omega, axis=self.projection_direction()) annular_sector.move_arc_center_to(self.get_source_point()) return annular_sector
def new_sector(self, r, dr, lower_angle, upper_angle): alpha = self.max_opacity * self.opacity_function(r) annular_sector = AnnularSector( inner_radius=r, outer_radius=r + dr, color=self.color, fill_opacity=alpha, start_angle=lower_angle, angle=upper_angle - lower_angle ) # rotate (not project) it into the viewing plane rotation_matrix = z_to_vector(self.projection_direction()) annular_sector.apply_matrix(rotation_matrix) # now rotate it inside that plane rotated_RIGHT = np.dot(RIGHT, rotation_matrix.T) projected_RIGHT = self.project(RIGHT) omega = angle_between_vectors(rotated_RIGHT, projected_RIGHT) annular_sector.rotate(omega, axis=self.projection_direction()) annular_sector.move_arc_center_to(self.get_source_point()) return annular_sector
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
def rotate_to_match_vector(self, vector): #rotates a 2D plane such that it's normal matches a 3D vector rotation_axis = np.cross(vector, OUT) angle = angle_between_vectors(vector, OUT) self.rotate_in_place(-angle, axis=rotation_axis)