def ngon_to_triangles(face: Iterable['Vertex']) -> Iterable[Sequence[Vec3]]: face = [Vec3(v) for v in face] if face[0].isclose(face[-1]): # closed shape center = Vec3.sum(face[:-1]) / (len(face) - 1) else: center = Vec3.sum(face) / len(face) face.append(face[0]) for v1, v2 in zip(face[:-1], face[1:]): yield v1, v2, center
def ngon_to_triangles(face: Iterable["Vertex"]) -> Iterable[Sequence[Vec3]]: _face = Vec3.list(face) if _face[0].isclose(_face[-1]): # closed shape center = Vec3.sum(_face[:-1]) / (len(_face) - 1) else: center = Vec3.sum(_face) / len(_face) _face.append(_face[0]) for v1, v2 in zip(_face[:-1], _face[1:]): yield v1, v2, center
def subdivide_face(face: Sequence[Union[Vec3, Vec2]], quads=True) -> Iterable[List[Vec3]]: """ Yields new subdivided faces. Creates new faces from subdivided edges and the face midpoint by linear interpolation. Args: face: a sequence of vertices, :class:`Vec2` and :class:`Vec3` objects supported. quads: create quad faces if ``True`` else create triangles """ if len(face) < 3: raise ValueError('3 or more vertices required.') len_face = len(face) mid_pos = Vec3.sum(face) / len_face subdiv_location = [ face[i].lerp(face[(i + 1) % len_face]) for i in range(len_face) ] for index, vertex in enumerate(face): if quads: yield vertex, subdiv_location[index], mid_pos, subdiv_location[ index - 1] else: yield subdiv_location[index - 1], vertex, mid_pos yield vertex, subdiv_location[index], mid_pos
def spherical_envelope(points: Sequence["Vertex"]) -> Tuple[Vec3, float]: """Calculate the spherical envelope for the given points. Returns the centroid (a.k.a. geometric center) and the radius of the enclosing sphere. .. note:: The result does not represent the minimal bounding sphere! .. versionadded:: 0.18 """ centroid = Vec3.sum(points) / len(points) radius = max(centroid.distance(p) for p in points) return centroid, radius
def subdivide_ngons( faces: Iterable[Sequence[Union[Vec3, Vec2]]]) -> Iterable[List[Vec3]]: """ Yields only triangles or quad faces, subdivides ngons into triangles. Args: faces: iterable of faces as sequence of :class:`Vec2` and :class:`Vec3` objects """ for face in faces: if len(face) < 5: yield face else: mid_pos = Vec3.sum(face) / len(face) for index, vertex in enumerate(face): yield face[index - 1], vertex, mid_pos
def recenter() -> Iterator[AnyVec]: for cluster_points in clusters.values(): yield Vec3.sum(cluster_points) / len(cluster_points) if len(clusters) < k: # refill centroids if required yield from random.sample(points, k - len(clusters))