diff --git a/.typo-ci.yml b/.typo-ci.yml index 5f5bf17e2..c8ebe4a4f 100644 --- a/.typo-ci.yml +++ b/.typo-ci.yml @@ -30,6 +30,7 @@ excluded_words: - shutil - functools - boltons + - meshio # matplotlib ---------------------------------- - gca - mpl @@ -118,6 +119,9 @@ excluded_words: - numpydoc # markdown / latex ---------------------------- - cdot + # file extensions ----------------------------- + - vtk + - stl # other --------------------------------------- - addopts - csm diff --git a/CHANGELOG.md b/CHANGELOG.md index d825667a1..cc752260c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -79,6 +79,7 @@ pending [ASDF #916](https://github.com/asdf-format/asdf/issues/916) [[#224]](https://github.com/BAMWelDX/weldx/pull/224) - set minimum Python version to 3.8 [[#229]](https://github.com/BAMWelDX/weldx/pull/229)[[#255]](https://github.com/BAMWelDX/weldx/pull/255) - only import some packages upon first use [[#247]](https://github.com/BAMWelDX/weldx/pull/247) +- Add [meshio](https://pypi.org/project/meshio/) as new dependency [#265](https://github.com/BAMWelDX/weldx/pull/265) ## 0.2.2 (30.11.2020) diff --git a/environment.yml b/environment.yml index 3fe7ef3e8..7a66ae0e3 100644 --- a/environment.yml +++ b/environment.yml @@ -16,6 +16,7 @@ dependencies: - asdf>=2.7 - openpyxl - fs + - meshio # graph packages - networkx # Code quality diff --git a/setup.cfg b/setup.cfg index e7d8ca997..979b3fcb3 100644 --- a/setup.cfg +++ b/setup.cfg @@ -51,6 +51,7 @@ install_requires = fs ipywidgets k3d + meshio include_package_data = True diff --git a/tests/test_geometry.py b/tests/test_geometry.py index 4cef4f63f..587a1b769 100644 --- a/tests/test_geometry.py +++ b/tests/test_geometry.py @@ -2,6 +2,8 @@ import copy import math +from pathlib import Path +from tempfile import TemporaryDirectory from typing import List, Union import numpy as np @@ -2927,3 +2929,34 @@ def test_class_creation_exceptions(arguments, exception_type, test_name): """ with pytest.raises(exception_type): SpatialData(*arguments) + + @staticmethod + @pytest.mark.parametrize( + "filename", + ["test.ply", "test.stl", "test.vtk", Path("test.stl")], + ) + def test_read_write_file(filename: Union[str, Path]): + """Test the `from_file` and `write_to_file` functions. + + The test simply creates a `SpatialData` instance, writes it to a file and reads + it back. The result is compared to the original object. + + Parameters + ---------- + filename : + Name of the file + + """ + points = [[0, 0, 0], [1, 0, 0], [1, 1, 0], [0, 1, 0]] + triangles = [[0, 1, 2], [2, 3, 0]] + + data = SpatialData(points, triangles) + with TemporaryDirectory(dir=Path(__file__).parent) as tmpdirname: + filepath = f"{tmpdirname}/{filename}" + if isinstance(filename, Path): + filepath = Path(filepath) + data.write_to_file(filepath) + data_read = SpatialData.from_file(filepath) + + assert np.allclose(data.coordinates, data_read.coordinates) + assert np.allclose(data.triangles, data_read.triangles) diff --git a/weldx/geometry.py b/weldx/geometry.py index 17534ade8..ecbbc822a 100644 --- a/weldx/geometry.py +++ b/weldx/geometry.py @@ -4,8 +4,10 @@ import copy import math from dataclasses import dataclass +from pathlib import Path from typing import TYPE_CHECKING, Dict, List, Tuple, Union +import meshio import numpy as np import pint from xarray import DataArray @@ -2328,6 +2330,26 @@ def __post_init__(self): if not self.triangles.ndim == 2: raise ValueError("SpatialData triangulation must be a 2d array") + @staticmethod + def from_file(file_name: Union[str, Path]) -> "SpatialData": + """Create an instance from a file. + + Parameters + ---------- + file_name : + Name of the source file. + + Returns + ------- + SpatialData: + New `SpatialData` instance + + """ + mesh = meshio.read(file_name) + triangles = mesh.cells_dict.get("triangle") + + return SpatialData(mesh.points, triangles) + @staticmethod def from_geometry_raster(geometry_raster: np.ndarray) -> "SpatialData": """Triangulate rasterized Geometry Profile. @@ -2399,3 +2421,19 @@ def plot( label=label, show_wireframe=show_wireframe, ) + + def write_to_file(self, file_name: Union[str, Path]): + """Write spatial data into a file. + + The extension prescribes the output format. + + Parameters + ---------- + file_name : + Name of the file + + """ + mesh = meshio.Mesh( + points=self.coordinates.data, cells={"triangle": self.triangles} + ) + mesh.write(file_name)