"""Pre-processing utilities."""
import pyvista as pv
import trimesh
import vtk
vtk.vtkLogger.SetStderrVerbosity(vtk.vtkLogger.VERBOSITY_OFF)
def _get_scaling_factor(
mesh: pv.PolyData,
max_dimensions: tuple[float, float, float] = (10.0, 2.0, 1.0)
) -> float:
"""
Calculate the scaling factor required to normalize the mesh dimensions.
Returns the most restrictive scaling factor to ensure the entire mesh fits
within the specified bounds.
length, width, height = max_dimensions
Args:
mesh (pv.PolyData): The input mesh to be normalized.
max_dimensions: The maximum dimensions of the object.
Returns:
float: The scaling factor to apply to the mesh.
"""
x_dimension = mesh.bounds[1] - mesh.bounds[0]
y_dimension = mesh.bounds[3] - mesh.bounds[2]
z_dimension = mesh.bounds[5] - mesh.bounds[4]
# Calculate the scaling factors for each axis
scaling_factor_x = max_dimensions[0] / x_dimension
scaling_factor_y = max_dimensions[1] / y_dimension
scaling_factor_z = max_dimensions[2] / z_dimension
# Use the most restrictive scaling factor
return min(scaling_factor_x, scaling_factor_y, scaling_factor_z)
[docs]
def normalize_mesh(
mesh: pv.PolyData,
max_dimensions: tuple[float, float, float] = (10.0, 2.0, 1.0)
) -> pv.PolyData:
"""
Normalize the dimensions of the input mesh.
The mesh is scaled to fit within the specified bounds of the wind tunnel.
length, width, height = max_dimensions
Args:
mesh (pv.PolyData): The input mesh to be normalized.
max_dimensions: The maximum dimensions of the object.
Returns:
pv.PolyData: The normalized mesh.
float: The scaling factor applied to the mesh.
"""
scaling_factor = _get_scaling_factor(mesh, max_dimensions)
mesh = mesh.scale(scaling_factor)
return mesh, scaling_factor
[docs]
def move_mesh_to_origin(mesh: pv.PolyData):
"""
Translate the mesh to the origin of the wind tunnel.
Args:
mesh (pv.PolyData): The input mesh to be translated.
Returns:
pv.PolyData: The translated mesh.
tuple[float, float, float]: The displacement vector applied to the mesh.
"""
# Get the z-coordinate of the lowest point of the mesh
z_displace = mesh.bounds[4]
# Get the y-coordinate of the center of the mesh
y_displace = (mesh.bounds[2] + mesh.bounds[3]) / 2
# Get the x-coordinate of the center of the mesh
x_displace = (mesh.bounds[0] + mesh.bounds[1]) / 2
displace_vector = (-x_displace, -y_displace, -z_displace)
mesh = mesh.translate(displace_vector)
return mesh, displace_vector
[docs]
def compute_projected_area(mesh: pv.PolyData, face_normal):
"""Compute the projected area of an object.
Args:
mesh: pyvista.PolyData mesh representing the object.
"""
# Compute roughly the projected area of the object. Slice the object mesh at
# its center, which provides an outline curve of the object. Then fill it
# with a simple mesh and compute the area of the filled mesh.
mesh_slice = mesh.slice(origin=mesh.center, normal=face_normal)
filled_slice = mesh_slice.delaunay_2d()
area = filled_slice.area
return area
[docs]
def compute_object_length(mesh: pv.PolyData):
# Length of the object in x-direction
length = mesh.bounds[1] - mesh.bounds[0]
return length
[docs]
def save_mesh_obj(mesh, dest_object_path):
trimesh_mesh = trimesh.Trimesh(vertices=mesh.points,
faces=mesh.faces.reshape((-1, 4))[:, 1:])
trimesh.exchange.export.export_mesh(trimesh_mesh, dest_object_path)