Source code for vtk_openCARP_methods_ibt.openCARP.importing

import argparse
import os

import numpy as np
import vtk

from ..vtk_methods.helper_methods import add_vectors_to_vtk
from ..vtk_methods.exporting import vtk_polydata_writer


[docs] def load_pts(pts_file): """ Loads a .pts file and converts it to an vtkPoints array The file is expected to start with the number of points, then lists the point coordinates. :param pts_file: the .pts file name with extension .pts :return: vtkPoints array """ if not os.path.isfile(pts_file): raise FileNotFoundError(f"The .pts file {pts_file} does not exist!") if not pts_file.endswith(".pts"): raise ValueError(f"The .pts file {pts_file} is not a .pts file!") vtk_points = vtk.vtkPoints() pts_data = np.loadtxt(pts_file, skiprows=1, usecols=(0, 1, 2)) num_points = int(np.loadtxt(pts_file, max_rows=1)) if pts_data.shape[0] != num_points: raise ValueError("The number of points declared is not equal to the number of points in the .pts file!") for point in pts_data: vtk_points.InsertNextPoint(point) return vtk_points
OPENCARP_TO_VTK = { "Ln": (vtk.VTK_LINE, 2), "Tr": (vtk.VTK_TRIANGLE, 3), "Tt": (vtk.VTK_TETRA, 4), "Hx": (vtk.VTK_HEXAHEDRON, 8), "Py": (vtk.VTK_PYRAMID, 5), "Pr": (vtk.VTK_WEDGE, 6), "Qd": (vtk.VTK_QUAD, 4), }
[docs] def load_elem(elem_file_path): if not os.path.isfile(elem_file_path): raise FileNotFoundError(f"The .elem file {elem_file_path} does not exist!") if not elem_file_path.endswith(".elem"): raise ValueError(f"The .elem file {elem_file_path} is not a .elem file!") with open(elem_file_path, 'r') as f: lines = f.readlines() num_elements = int(lines[0]) vtk_cells = vtk.vtkCellArray() cell_types = [] element_tags = [] for line in lines[1:]: parts = line.split() if not parts: continue elem_type_str = parts[0] if elem_type_str not in OPENCARP_TO_VTK: raise ValueError(f"Unknown element type: {elem_type_str}") vtk_type, expected_nodes = OPENCARP_TO_VTK[elem_type_str] node_ids = [int(parts[i]) for i in range(1, expected_nodes + 1)] cell = vtk.vtkIdList() for nid in node_ids: cell.InsertNextId(nid) vtk_cells.InsertNextCell(cell) cell_types.append(vtk_type) element_tags.append(int(parts[expected_nodes + 1])) if vtk_cells.GetNumberOfCells() != num_elements: raise ValueError("Declared element count does not match actual element count!") return vtk_cells, element_tags, cell_types
[docs] def load_lon(lon_file_path): if not os.path.exists(lon_file_path): raise FileNotFoundError(f"The file {lon_file_path} does not exist") with open(lon_file_path, "r") as file: lines = file.readlines() num_arrays = int(lines[0].strip()) data = np.loadtxt(lines[1:]) if num_arrays == 1: fibers = data.reshape(-1, 3) sheets = None elif num_arrays == 2: fibers = data[:,:3] sheets = data[:,3:] else: raise ValueError("Invalid .lon file: must have 1 or 2 vector arrays.") return fibers, sheets
[docs] def convert_openCARP_to_vtk_single_name(file_name:str): if '.' in file_name: VALID_EXTENSIONS = {'pts', 'elem', 'lon',''} # Split filename and extension base_name, ext = os.path.splitext(file_name) ext = ext.lower().lstrip('.') # remove leading dot and lowercase if ext not in VALID_EXTENSIONS: raise ValueError( f"Invalid file extension: .{ext}. Must be one of: {', '.join(VALID_EXTENSIONS)}" ) else: base_name = file_name pts_file = f"{base_name}.pts" elem_file = f"{base_name}.elem" lon_file = f"{base_name}.lon" if os.path.exists(lon_file): return convert_openCARP_to_vtk(pts_file, elem_file, lon_file) else: return convert_openCARP_to_vtk(pts_file, elem_file)
[docs] def convert_openCARP_to_vtk(pts_file_path, elem_file_path, lon_file_path="",poly_data=True): """ Convert a .pts and .elem file to a .vtk file. :param pts_file_path: Filepath of the .pts file with .pts extension :param elem_file_path: Filepath of the .elem file with .elem extension :param lon_file_path: Filepath of the .lon file with .lon extension. Optional. :return: vtk polydata object """ vtk_points = load_pts(pts_file_path) vtk_cells, element_tags, cell_types = load_elem(elem_file_path) if poly_data: mesh = vtk.vtkPolyData() mesh.SetPoints(vtk_points) mesh.SetPolys(vtk_cells) cell_data_array = vtk.vtkIntArray() cell_data_array.SetName("elemTag") for cell_data in element_tags: cell_data_array.InsertNextValue(cell_data) mesh.GetCellData().SetScalars(cell_data_array) else: mesh = vtk.vtkUnstructuredGrid() mesh.SetPoints(vtk_points) # SetCells requires a vtkUnsignedCharArray of type codes cell_type_array = vtk.vtkUnsignedCharArray() for ct in cell_types: cell_type_array.InsertNextValue(ct) mesh.SetCells(cell_type_array, vtk_cells) tags = vtk.vtkIntArray() tags.SetName("ElementTag") for t in element_tags: tags.InsertNextValue(t) mesh.GetCellData().AddArray(tags) if lon_file_path: fibers, sheet = load_lon(lon_file_path) add_vectors_to_vtk(mesh, fibers, "fiber") if sheet is not None: add_vectors_to_vtk(mesh, sheet, "sheet") return mesh
[docs] def parse_args(): parser = argparse.ArgumentParser(description="Convert openCARP mesh files to VTK format.") # Arguments with -- for flexible naming parser.add_argument('--pts-file', type=str, required=True, default="data/cube.pte", help="Path to the .pts file containing node positions") parser.add_argument('--elem-file', type=str, required=True, default="data/cube.elem", help="Path to the .elem file containing element connectivity") parser.add_argument('--lon-file', type=str, required=True, default="data/cube.lon", help="Path to the .lon file containing fiber (and sheet direction)") parser.add_argument('--output-file', type=str, required=True, help="Path for the output VTK file") return parser.parse_args()
if __name__ == "__main__": args = parse_args() vtk_mesh = convert_openCARP_to_vtk(args.pts_file, args.elem_file) vtk_polydata_writer(args.output_file, vtk_mesh)