Source code for ddg.datastructures.indexedfaceset.utils

import numpy as np

from ddg.datastructures.halfedge import surface
from ddg.datastructures.indexedfaceset import ifs as IFS


[docs]def diags_from_faces(faces): """ Calculates diagonals of the faces of a quadrilateral mesh. Parameters ---------- faces : np.ndarray Array of faces In case only one face exists, it should be passed as a 2D array Returns ------- tuple a tuple of 2d array of two sets of diagonals of the quads """ if not isinstance(faces, np.ndarray): raise ValueError("The faces must be numpy arrays") if faces.ndim != 2: raise ValueError( "The face array must be a 2D numpy array. Even only one face exists, it" " must be passed as a 2D array" ) if faces.shape[1] != 4: raise ValueError("the function can only be called for quadrilateral mesh") diag1 = np.column_stack((faces[:, 0], faces[:, 2])) diag2 = np.column_stack((faces[:, 1], faces[:, 3])) return diag1, diag2
[docs]def is_manifold(ifs): """ Returns whether every edge of the non oriented indexed face set is contained in at most two faces. """ # is_manifold for oriented face sets gets checked in construction and # prevented if neccessary. facesPerEdge = dict() for f in ifs.face_list(): for e in ifs.face_boundary(f): e = tuple(sorted(e)) if e in facesPerEdge: facesPerEdge[e] += 1 else: facesPerEdge[e] = 1 if facesPerEdge[e] > 2: return False return True
[docs]def orient(src_ifs, face_map=dict()): """ Try to orient a given indexed face set. Raises Value Error if the given set is not orientable. """ if not is_manifold(src_ifs): raise NonManifoldException() # 0 : not seen # 1 : in the queue # 2 : done unseen_src_faces = {tuple(f): 0 for f in src_ifs.face_list()} n_visited_faces = 0 oriented_ifs = IFS.OrientedIndexedFaceSet([]) oriented_faces_queue = [IFS.OrientedFace(src_ifs.face_list()[0], orientation=1)] while n_visited_faces < src_ifs.number_of_faces(): if len(oriented_faces_queue) == 0: next_face = next( iter( filter(lambda x: unseen_src_faces[x] == 0, unseen_src_faces.keys()) ) ) oriented_faces_queue.append( IFS.OrientedFace(tuple(next_face), orientation=1) ) oriented_face = oriented_faces_queue.pop(0) unseen_src_faces[oriented_face.get_tuple()] = 2 n_visited_faces += 1 try: oriented_ifs.add_face(oriented_face.get_oriented_tuple()) neighboring_faces = [ n_face for n_face in src_ifs.neighboring_faces_with_orientation( oriented_face.get_tuple() ) if not isinstance(n_face, IFS.NoneFace) ] for neighboring_face in [ nf for nf in neighboring_faces if unseen_src_faces[nf.get_tuple()] == 0 ]: neighboring_face.set_orientation( -1 * neighboring_face.get_orientation() * oriented_face.get_orientation() ) oriented_faces_queue.append(neighboring_face) unseen_src_faces[neighboring_face.get_tuple()] = 1 face_map[oriented_face.get_oriented_tuple()] = oriented_face.get_tuple() except ValueError: raise NonOrientableException() return oriented_ifs
[docs]def indexed_face_set_to_surface( ifs, vertex_index_attribute="ifs_index", face_index_attribute="ifs_face_index" ): """ Convert an indexed face set to a half edge data structure. If the indexed face set can not be converted an empty half edge data structure is returned. """ hds = surface.Surface() if vertex_index_attribute is not None: hds.verts.add_attribute(vertex_index_attribute) if face_index_attribute is not None: hds.faces.add_attribute(face_index_attribute) faceMap = dict() orientedIFS = orient(ifs, faceMap) vertices = set([v for l in orientedIFS.face_list() for v in l]) vertexMap = dict() for v in vertices: vertex = hds.verts() vertexMap[v] = vertex if vertex_index_attribute is not None: setattr(vertex, vertex_index_attribute, v) original_face_index_map = {f: i for i, f in enumerate(ifs.face_list())} edgeMap = dict() for f in orientedIFS.face_list(): edges = [] face = hds.faces() original_face = faceMap[tuple(f)] f_index = original_face_index_map[original_face] if face_index_attribute is not None: setattr(face, face_index_attribute, f_index) for e in ifs.face_boundary(f): edge = hds.edges() edge.face = face edgeMap[e] = edge edges.append(edge) head = vertexMap[e[1]] edge.head = head if head.edge is None: head.edge = edge if face.edge is None: face.edge = edge for i in range(len(edges)): edges[i].nex = edges[(i + 1) % len(edges)] edges[(i + 1) % len(edges)].pre = edges[i] boundaryEdges = [] for eTuple in list(edgeMap): edge = edgeMap[eTuple] if edge.opp is not None: continue else: eOppTuple = tuple(reversed(eTuple)) if eOppTuple in edgeMap: oppEdge = edgeMap[eOppTuple] oppEdge.opp = edge edge.opp = oppEdge else: oppEdge = hds.edges() oppEdge.head = vertexMap[eOppTuple[1]] boundaryEdges.append(eOppTuple) edgeMap[eOppTuple] = oppEdge oppEdge.opp = edge edge.opp = oppEdge for eTuple in boundaryEdges: nextEdgeTuple = _find_next_tuple(eTuple, boundaryEdges) edgeMap[eTuple].nex = edgeMap[nextEdgeTuple] edgeMap[nextEdgeTuple].pre = edgeMap[eTuple] return hds
[docs]def face_boundary(face): return tuple(zip(face, tuple(list(face[1:]) + [face[0]])))
def _find_next_tuple(edge, edgeList): targetIndex = edge[1] nextEdges = [] for e in edgeList: if e[0] == targetIndex: nextEdges.append(e) if len(nextEdges) != 1: raise BoundaryException( "Head of edge " + str(edge) + " does not meet tail of ONE edge in " + str(edgeList) ) else: return nextEdges[0]
[docs]class BoundaryException(Exception): pass
[docs]class NonManifoldException(Exception): pass
[docs]class NonOrientableException(Exception): pass