Source code for gustaf.faces

"""gustaf/gustaf/faces.py."""
import numpy as np

from gustaf import helpers, settings, show, utils
from gustaf.edges import Edges
from gustaf.helpers.options import Option

# special types for face texture option
try:
    import vedo

    vedoPicture = vedo.Picture
    # there are other ways to get here, but this is exact path for our use
    vtkTexture = vedo.mesh.vtk.vtkTexture
except ImportError as err:
    vedoPicture = helpers.raise_if.ModuleImportRaiser("vedo", err)
    vtkTexture = helpers.raise_if.ModuleImportRaiser("vedo", err)


[docs] class FacesShowOption(helpers.options.ShowOption): """ Show options for vertices. """ _valid_options = helpers.options.make_valid_options( *helpers.options.vedo_common_options, Option("vedo", "lw", "Width of edges (lines) in pixel units.", (int,)), Option( "vedo", "lc", "Color of edges (lines).", (int, str, tuple, list) ), Option( "vedo", "texture", "Texture of faces in array, vedo.Picture, vtk.vtkTexture, " "or path to an image.", (np.ndarray, tuple, list, str, vedoPicture, vtkTexture), ), ) _helps = "Faces" def _initialize_vedo_showable(self): """ Initializes Faces as vedo.Mesh Parameters ---------- None Returns ------- faces: vedo.Mesh """ faces = show.vedo.Mesh( [self._helpee.const_vertices, self._helpee.const_faces], ) for option in ["lw", "lc", "texture"]: val = self.get(option, False) if val: getattr(faces, option)(val) return faces
[docs] class Faces(Edges): kind = "face" const_edges = helpers.raise_if.invalid_inherited_attr( "Edges.const_edges", __qualname__, property_=True, ) update_edges = helpers.raise_if.invalid_inherited_attr( "Edges.update_edges", __qualname__, property_=False, ) dashed = helpers.raise_if.invalid_inherited_attr( "Edges.dashed", __qualname__, property_=False, ) __slots__ = ( "_faces", "_const_faces", "BC", ) __show_option__ = FacesShowOption __boundary_class__ = Edges def __init__( self, vertices=None, faces=None, elements=None, copy=True, ): """Faces. It has vertices and faces. Faces could be triangles or quadrilaterals. Parameters ----------- vertices: (n, d) np.ndarray faces: (n, 3) or (n, 4) np.ndarray """ super().__init__(vertices=vertices, copy=copy) if faces is not None: self.faces = faces elif elements is not None: self.faces = elements self.BC = dict()
[docs] @helpers.data.ComputedMeshData.depends_on(["elements"]) def edges(self): """Edges from here aren't main property. So this needs to be computed. Parameters ----------- None Returns -------- edges: (n, 2) np.ndarray """ self._logd("computing edges") faces = self._get_attr("faces") return utils.connec.faces_to_edges(faces)
@property def whatami(self): """Determines whatami. Parameters ----------- None Returns -------- None """ return type(self).whatareyou(self)
[docs] @classmethod def whatareyou(cls, face_obj): """classmethod that tells you if the Faces is tri or quad or invalid kind. Parameters ----------- face_obj: Faces Returns -------- whatareyou: str """ if not cls.kind.startswith(face_obj.kind): raise TypeError("Given obj is not {cls.__qualname__}") if face_obj.faces.shape[1] == 3: return "tri" elif face_obj.faces.shape[1] == 4: return "quad" else: raise ValueError( "Invalid faces connectivity shape. It should be (n, 3) or " f"(n, 4), but given: {face_obj.faces.shape}" )
@property def faces( self, ): """Returns faces. Parameters ----------- None Returns -------- faces """ self._logd("returning faces") return self._faces @faces.setter def faces(self, fs): """Faces setter. Similar to vertices, this will be a tracked array. Parameters ----------- fs: (n, 2) np.ndarray Returns -------- None """ self._logd("setting faces") self._faces = helpers.data.make_tracked_array( fs, settings.INT_DTYPE, self.setter_copies, ) # shape check if fs is not None: utils.arr.is_one_of_shapes( fs, ((-1, 3), (-1, 4)), strict=True, ) # same, but non-writeable view of tracked array self._const_faces = self._faces.view() self._const_faces.flags.writeable = False @property def const_faces(self): """Returns non-writeable view of faces. Parameters ----------- None Returns -------- const_faces: (n, 2 """ return self._const_faces
[docs] @helpers.data.ComputedMeshData.depends_on(["elements"]) def sorted_faces(self): """Similar to edges_sorted but for faces. Parameters ----------- None Returns -------- sorted_faces: (self.faces.shape) np.ndarray """ faces = self._get_attr("faces") return np.sort(faces, axis=1)
[docs] @helpers.data.ComputedMeshData.depends_on(["elements"]) def unique_faces(self): """Returns a namedtuple of unique faces info. Similar to unique_edges. Parameters ----------- None Returns -------- unique_info: Unique2DIntegers valid attributes are {values, ids, inverse, counts} """ unique_info = utils.connec.sorted_unique( self.sorted_faces(), sorted_=True ) faces = self._get_attr("faces") unique_info.values[:] = faces[unique_info.ids] return unique_info
[docs] @helpers.data.ComputedMeshData.depends_on(["elements"]) def single_faces(self): """Returns indices of very unique faces: faces that appear only once. For well constructed volumes, this can be considered as surfaces. Parameters ----------- None Returns -------- single_faces: (m,) np.ndarray """ unique_info = self.unique_faces() return unique_info.ids[unique_info.counts == 1]
[docs] def update_faces(self, *args, **kwargs): """Alias to update_elements.""" self.update_elements(*args, **kwargs)
[docs] def to_edges(self, unique=True): """Returns Edges obj. Parameters ----------- unique: bool Default is True. If True, only takes unique edges. Returns -------- edges: Edges """ return Edges( self.vertices, edges=self.unique_edges().values if unique else self.edges(), )