Source code for dtcc_model.geometry.mesh

# Copyright(C) 2023 Anders Logg
# Licensed under the MIT License

import numpy as np
from typing import Union
from dataclasses import dataclass, field
from inspect import getmembers, isfunction, ismethod

from .geometry import Geometry, Bounds
from dtcc_model import dtcc_pb2 as proto


[docs] @dataclass class Mesh(Geometry): """Represents an unstructured triangular mesh in 3D. The Mesh class represents a 3D triangular mesh, which consists of vertices and triangular faces. Attributes ---------- vertices : np.ndarray An array of vertices in 3D space. faces : np.ndarray An array of triangular faces defined by vertex indices. markers : np.ndarray An array of markers or labels associated with the faces. """ vertices: np.ndarray = field(default_factory=lambda: np.empty(0, dtype=np.float64)) faces: np.ndarray = field(default_factory=lambda: np.empty(0, dtype=np.int64)) markers: np.ndarray = field(default_factory=lambda: np.empty(0))
[docs] def __str__(self): """Return a string representation of the DTCC Mesh Returns ------- str A string describing the Mesh object. """ return f"DTCC Mesh with {len(self.vertices)} vertices and {len(self.faces)} face(s)"
@property def num_vertices(self) -> int: """Get the number of vertices in the mesh. Returns ------- int The number of vertices in the mesh. """ return len(self.vertices)
[docs] def calculate_bounds(self): """Calculate the bounding box of the mesh.""" if len(self.vertices) < 3: self._bounds = Bounds() return self._bounds self._bounds = Bounds( xmin=np.min(self.vertices[:, 0]), ymin=np.min(self.vertices[:, 1]), zmin=np.min(self.vertices[:, 2]), xmax=np.max(self.vertices[:, 0]), ymax=np.max(self.vertices[:, 1]), zmax=np.max(self.vertices[:, 2]), ) return self._bounds
@property def num_faces(self) -> int: """Calculate the number of faces in the mesh. Returns ------- int The number of faces in the mesh. """ return len(self.faces)
[docs] def to_proto(self) -> proto.Geometry: """Return a protobuf representation of the Mesh. Returns ------- proto.Geometry A protobuf representation of the Mesh as a Geometry. """ # Handle Geometry fields pb = Geometry.to_proto(self) # Handle specific fields _pb = proto.Mesh() _pb.vertices.extend(self.vertices.flatten()) _pb.faces.extend(self.faces.flatten()) pb.mesh.CopyFrom(_pb) return pb
[docs] def from_proto(self, pb: Union[proto.Geometry, bytes]): """Initialize Mesh from a protobuf representation. Parameters ---------- pb: Union[proto.Geometry, bytes] The protobuf message or its serialized bytes representation. """ # Handle byte representation if isinstance(pb, bytes): pb = proto.Geometry.FromString(pb) # Handle Geometry fields Geometry.from_proto(self, pb) # Handle specific fields _pb = pb.mesh self.vertices = np.array(_pb.vertices).reshape((-1, 3)) self.faces = np.array(_pb.faces, dtype=np.int64).reshape((-1, 3))
[docs] @dataclass class VolumeMesh(Geometry): """Represents an unstructured tetrahedral mesh in 3D. The VolumeMesh class represents a 3D volumetric mesh, which consists of vertices and tetrahedral cells. Attributes ---------- vertices : np.ndarray An array of vertices in 3D space. cells : np.ndarray An array of tetrahedral cells defined by vertex indices. markers : np.ndarray An array of markers or labels associated with th cells. """ vertices: np.ndarray = field(default_factory=lambda: np.empty(0)) cells: np.ndarray = field(default_factory=lambda: np.empty(0)) markers: np.ndarray = field(default_factory=lambda: np.empty(0))
[docs] def __str__(self): """Return a string representation of the DTCC VolumeMesh, containing the number of vertices and cells. Returns ------- str A string describing the VolumeMesh object. """ return f"DTCC VolumeMesh with {len(self.vertices)} vertices and {len(self.cells)} cell(s)"
[docs] def calculate_bounds(self) -> Bounds: """Calculate the bounding box of the mesh.""" self._bounds = Bounds( xmin=np.min(self.vertices[:, 0]), ymin=np.min(self.vertices[:, 1]), zmin=np.min(self.vertices[:, 2]), xmax=np.max(self.vertices[:, 0]), ymax=np.max(self.vertices[:, 1]), zmax=np.max(self.vertices[:, 2]), ) return self._bounds
@property def num_vertices(self) -> int: """Get the number of vertices in the volume mesh. Returns ------- int The number of vertices in the volume mesh. """ return len(self.vertices) @property def num_cells(self) -> int: """Get the number of cells or elements in the volume mesh. Returns ------- int The number of cells or elements in the volume mesh. """ return len(self.cells)
[docs] def to_proto(self) -> proto.Geometry: """Return a protobuf representation of the VolumeMesh. Returns ------- proto.Geometry A protobuf representation of the VolumeMesh as a Geometry. """ # Handle Geometry fields pb = Geometry.to_proto(self) # Handle specific fields _pb = proto.VolumeMesh() _pb.vertices.extend(self.vertices.flatten()) _pb.cells.extend(self.cells.flatten()) pb.mesh.CopyFrom(_pb) return pb
[docs] def from_proto(self, pb: Union[proto.Geometry, bytes]): """Initialize VolumeMesh from a protobuf representation. Parameters ---------- pb: Union[proto.Geometry, bytes] The protobuf message or its serialized bytes representation. """ # Handle byte representation if isinstance(pb, bytes): pb = proto.FromString(pb) # Handle Geometry fields Geometry.from_proto(self, pb) # Handle specific fields _pb = pb.volume_mesh self.vertices = np.array(_pb.vertices).reshape((-1, 3)) self.cells = np.array(_pb.faces, dtype=np.int64).reshape((-1, 4))