From 7d9541edb283d78b1ab998bec9bb379cc26a406c Mon Sep 17 00:00:00 2001 From: Alexander Fabisch Date: Thu, 20 Jul 2023 16:52:45 +0200 Subject: [PATCH 01/15] Load collada files --- pytransform3d/visualizer/_artists.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/pytransform3d/visualizer/_artists.py b/pytransform3d/visualizer/_artists.py index 3aaec1b97..37ad3a212 100644 --- a/pytransform3d/visualizer/_artists.py +++ b/pytransform3d/visualizer/_artists.py @@ -554,7 +554,23 @@ class Mesh(Artist): """ def __init__(self, filename, A2B=np.eye(4), s=np.ones(3), c=None, convex_hull=False): - mesh = o3d.io.read_triangle_mesh(filename) + if filename.endswith(".dae"): + import collada + mesh = o3d.geometry.TriangleMesh() + try: + model = collada.Collada(filename) + for geometry in model.geometries: + for primitive in geometry.primitives: + p = o3d.geometry.TriangleMesh() + vertices = primitive.vertex + p.vertices = o3d.utility.Vector3dVector(vertices) + triangles = primitive.vertex_index + p.triangles = o3d.utility.Vector3iVector(triangles) + mesh += p + except: + warnings.warn("Parsing collada file '%s' failed" % filename) + else: + mesh = o3d.io.read_triangle_mesh(filename) if convex_hull: self.mesh = mesh.compute_convex_hull()[0] else: From 7ab3e538acb2b393c96291b06dfd4f5671239350 Mon Sep 17 00:00:00 2001 From: Alexander Fabisch Date: Thu, 27 Jul 2023 11:49:19 +0200 Subject: [PATCH 02/15] Extract mesh loader module --- pytransform3d/mesh_loader.py | 50 +++++++++++++++++++++ pytransform3d/plot_utils/_plot_functions.py | 20 +++------ pytransform3d/visualizer/_artists.py | 24 ++-------- 3 files changed, 59 insertions(+), 35 deletions(-) create mode 100644 pytransform3d/mesh_loader.py diff --git a/pytransform3d/mesh_loader.py b/pytransform3d/mesh_loader.py new file mode 100644 index 000000000..079f96c09 --- /dev/null +++ b/pytransform3d/mesh_loader.py @@ -0,0 +1,50 @@ +import numpy as np + + +def load_mesh(filename, convex_hull=False, return_as_open3d_mesh=False): + vertices = np.empty((0, 3), dtype=float) + triangles = np.empty((0, 3), dtype=int) + done = False + + try: + import trimesh + geometry = trimesh.load(filename) + done = True + + if isinstance(geometry, trimesh.Scene): + geometry = trimesh.util.concatenate( + list(geometry.geometry.values())) + if convex_hull: + geometry = geometry.convex_hull + + vertices = geometry.vertices + triangles = geometry.faces + + if return_as_open3d_mesh: + import open3d + return open3d.geometry.TriangleMesh( + open3d.utility.Vector3dVector(vertices), + open3d.utility.Vector3iVector(triangles)) + except ImportError: + pass + + try: + import open3d + mesh = open3d.io.read_triangle_mesh(filename) + done = True + + if convex_hull: + mesh = mesh.compute_convex_hull()[0] + + if return_as_open3d_mesh: + return mesh + else: + vertices = np.asarray(mesh.vertices) + triangles = np.asarray(mesh.triangles) + except ImportError: + pass + + if not done: + raise IOError("Could not load mesh from '%s'" % filename) + + return vertices, triangles diff --git a/pytransform3d/plot_utils/_plot_functions.py b/pytransform3d/plot_utils/_plot_functions.py index 171fe860c..ea4bb709e 100644 --- a/pytransform3d/plot_utils/_plot_functions.py +++ b/pytransform3d/plot_utils/_plot_functions.py @@ -6,6 +6,7 @@ from ._artists import Arrow3D from ..transformations import transform, vectors_to_points from ..rotations import unitx, unitz, perpendicular_to_vectors, norm_vector +from ..mesh_loader import load_mesh def plot_box(ax=None, size=np.ones(3), A2B=np.eye(4), ax_s=1, wireframe=True, @@ -316,8 +317,8 @@ def plot_mesh(ax=None, filename=None, A2B=np.eye(4), convex_hull=False, alpha=1.0, color="k"): """Plot mesh. - Note that this function requires the additional library 'trimesh'. - It will print a warning if trimesh is not available. + Note that this function requires the additional library to load meshes + such as trimesh or open3d. Parameters ---------- @@ -364,20 +365,11 @@ def plot_mesh(ax=None, filename=None, A2B=np.eye(4), "package directory.") return ax - try: - import trimesh - except ImportError: - warnings.warn( - "Cannot display mesh. Library 'trimesh' not installed.") - return ax - - mesh = trimesh.load(filename) - if convex_hull: - mesh = mesh.convex_hull - vertices = mesh.vertices * s + vertices, triangles = load_mesh(filename, convex_hull=convex_hull) + vertices = vertices * s vertices = np.hstack((vertices, np.ones((len(vertices), 1)))) vertices = transform(A2B, vertices)[:, :3] - vectors = np.array([vertices[[i, j, k]] for i, j, k in mesh.faces]) + vectors = np.array([vertices[[i, j, k]] for i, j, k in triangles]) if wireframe: surface = Line3DCollection(vectors) surface.set_color(color) diff --git a/pytransform3d/visualizer/_artists.py b/pytransform3d/visualizer/_artists.py index 37ad3a212..3c62c437b 100644 --- a/pytransform3d/visualizer/_artists.py +++ b/pytransform3d/visualizer/_artists.py @@ -6,6 +6,7 @@ from .. import rotations as pr from .. import transformations as pt from .. import urdf +from .. import mesh_loader class Artist: @@ -554,27 +555,8 @@ class Mesh(Artist): """ def __init__(self, filename, A2B=np.eye(4), s=np.ones(3), c=None, convex_hull=False): - if filename.endswith(".dae"): - import collada - mesh = o3d.geometry.TriangleMesh() - try: - model = collada.Collada(filename) - for geometry in model.geometries: - for primitive in geometry.primitives: - p = o3d.geometry.TriangleMesh() - vertices = primitive.vertex - p.vertices = o3d.utility.Vector3dVector(vertices) - triangles = primitive.vertex_index - p.triangles = o3d.utility.Vector3iVector(triangles) - mesh += p - except: - warnings.warn("Parsing collada file '%s' failed" % filename) - else: - mesh = o3d.io.read_triangle_mesh(filename) - if convex_hull: - self.mesh = mesh.compute_convex_hull()[0] - else: - self.mesh = mesh + self.mesh = mesh_loader.load_mesh( + filename, convex_hull=convex_hull, return_as_open3d_mesh=True) self.mesh.vertices = o3d.utility.Vector3dVector( np.asarray(self.mesh.vertices) * s) self.mesh.compute_vertex_normals() From 6bdbe760bfbb060d99645cf7dd14fbc0ea1e5e47 Mon Sep 17 00:00:00 2001 From: Alexander Fabisch Date: Thu, 27 Jul 2023 11:52:24 +0200 Subject: [PATCH 03/15] Use new trimesh API --- pytransform3d/mesh_loader.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pytransform3d/mesh_loader.py b/pytransform3d/mesh_loader.py index 079f96c09..6f2afad01 100644 --- a/pytransform3d/mesh_loader.py +++ b/pytransform3d/mesh_loader.py @@ -12,8 +12,7 @@ def load_mesh(filename, convex_hull=False, return_as_open3d_mesh=False): done = True if isinstance(geometry, trimesh.Scene): - geometry = trimesh.util.concatenate( - list(geometry.geometry.values())) + geometry = geometry.dump().sum() if convex_hull: geometry = geometry.convex_hull From 445cb5c082a96df23abff3f59ef1e96aa7348438 Mon Sep 17 00:00:00 2001 From: Alexander Fabisch Date: Thu, 27 Jul 2023 15:07:04 +0200 Subject: [PATCH 04/15] Use object interface --- pytransform3d/mesh_loader.py | 168 +++++++++++++++----- pytransform3d/plot_utils/_plot_functions.py | 6 +- pytransform3d/visualizer/_artists.py | 4 +- 3 files changed, 137 insertions(+), 41 deletions(-) diff --git a/pytransform3d/mesh_loader.py b/pytransform3d/mesh_loader.py index 6f2afad01..1b3cfe59d 100644 --- a/pytransform3d/mesh_loader.py +++ b/pytransform3d/mesh_loader.py @@ -1,49 +1,145 @@ +import abc + import numpy as np -def load_mesh(filename, convex_hull=False, return_as_open3d_mesh=False): - vertices = np.empty((0, 3), dtype=float) - triangles = np.empty((0, 3), dtype=int) - done = False +def load_mesh(filename, convex_hull=False): + """Load mesh from file. - try: - import trimesh - geometry = trimesh.load(filename) - done = True + Parameters + ---------- + filename : str + File in which the mesh is stored. - if isinstance(geometry, trimesh.Scene): - geometry = geometry.dump().sum() - if convex_hull: - geometry = geometry.convex_hull + convex_hull : bool, optional (default: False) + Compute convex hull of mesh. - vertices = geometry.vertices - triangles = geometry.faces + Returns + ------- + mesh : MeshBase + Mesh instance. + """ + mesh = _Trimesh(filename) + success = mesh.load() - if return_as_open3d_mesh: - import open3d - return open3d.geometry.TriangleMesh( - open3d.utility.Vector3dVector(vertices), - open3d.utility.Vector3iVector(triangles)) - except ImportError: - pass + if not success: + mesh = _Open3DMesh(filename) + success = mesh.load() + + if not success: + raise IOError("Could not load mesh from '%s'" % filename) + + if convex_hull: + mesh.convex_hull() + + return mesh + + +class MeshBase(abc.ABC): + """Abstract base class of meshes. + + Parameters + ---------- + filename : str + File in which the mesh is stored. + """ + def __init__(self, filename): + self.filename = filename + + @abc.abstractmethod + def load(self): + """Load mesh from file. + + Returns + ------- + success : bool + Has the mesh been loaded? + """ + + @abc.abstractmethod + def convex_hull(self): + """Compute convex hull of mesh.""" + + @abc.abstractmethod + def get_open3d_mesh(self): + """Return Open3D mesh. + + Returns + ------- + mesh : open3d.geometry.TriangleMesh + Open3D mesh. + """ - try: + @property + @abc.abstractmethod + def vertices(self): + """Vertices.""" + + @property + @abc.abstractmethod + def triangles(self): + """Triangles.""" + + +class _Trimesh(MeshBase): + def __init__(self, filename): + super(_Trimesh, self).__init__(filename) + self.mesh = None + + def load(self): + try: + import trimesh + except ImportError: + return False + + obj = trimesh.load(self.filename) + if isinstance(obj, trimesh.Scene): + obj = obj.dump().sum() + self.mesh = obj + return True + + def convex_hull(self): + self.mesh = self.mesh.convex_hull + + def get_open3d_mesh(self): import open3d - mesh = open3d.io.read_triangle_mesh(filename) - done = True + return open3d.geometry.TriangleMesh( + open3d.utility.Vector3dVector(self.vertices), + open3d.utility.Vector3iVector(self.triangles)) - if convex_hull: - mesh = mesh.compute_convex_hull()[0] + @property + def vertices(self): + return self.mesh.vertices - if return_as_open3d_mesh: - return mesh - else: - vertices = np.asarray(mesh.vertices) - triangles = np.asarray(mesh.triangles) - except ImportError: - pass + @property + def triangles(self): + return self.mesh.faces - if not done: - raise IOError("Could not load mesh from '%s'" % filename) - return vertices, triangles +class _Open3DMesh(MeshBase): + def __init__(self, filename): + super(_Open3DMesh, self).__init__(filename) + self.mesh = None + + def load(self): + try: + import open3d + except ImportError: + return False + self.mesh = open3d.io.read_triangle_mesh(self.filename) + return True + + def convex_hull(self): + assert self.mesh is not None + self.mesh = self.mesh.compute_convex_hull()[0] + + def get_open3d_mesh(self): + return self.mesh + + @property + def vertices(self): + return np.asarray(self.mesh.vertices) + + @property + def triangles(self): + return np.asarray(self.mesh.triangles) diff --git a/pytransform3d/plot_utils/_plot_functions.py b/pytransform3d/plot_utils/_plot_functions.py index ea4bb709e..c81f2ca3c 100644 --- a/pytransform3d/plot_utils/_plot_functions.py +++ b/pytransform3d/plot_utils/_plot_functions.py @@ -365,11 +365,11 @@ def plot_mesh(ax=None, filename=None, A2B=np.eye(4), "package directory.") return ax - vertices, triangles = load_mesh(filename, convex_hull=convex_hull) - vertices = vertices * s + mesh = load_mesh(filename, convex_hull=convex_hull) + vertices = mesh.vertices * s vertices = np.hstack((vertices, np.ones((len(vertices), 1)))) vertices = transform(A2B, vertices)[:, :3] - vectors = np.array([vertices[[i, j, k]] for i, j, k in triangles]) + vectors = np.array([vertices[[i, j, k]] for i, j, k in mesh.triangles]) if wireframe: surface = Line3DCollection(vectors) surface.set_color(color) diff --git a/pytransform3d/visualizer/_artists.py b/pytransform3d/visualizer/_artists.py index 3c62c437b..b675cfd01 100644 --- a/pytransform3d/visualizer/_artists.py +++ b/pytransform3d/visualizer/_artists.py @@ -555,8 +555,8 @@ class Mesh(Artist): """ def __init__(self, filename, A2B=np.eye(4), s=np.ones(3), c=None, convex_hull=False): - self.mesh = mesh_loader.load_mesh( - filename, convex_hull=convex_hull, return_as_open3d_mesh=True) + mesh = mesh_loader.load_mesh(filename, convex_hull=convex_hull) + self.mesh = mesh.get_open3d_mesh() self.mesh.vertices = o3d.utility.Vector3dVector( np.asarray(self.mesh.vertices) * s) self.mesh.compute_vertex_normals() From 84754b0d1cc7174417a7c6669f9f40be0fe46257 Mon Sep 17 00:00:00 2001 From: Alexander Fabisch Date: Thu, 27 Jul 2023 15:09:23 +0200 Subject: [PATCH 05/15] Add pycollada to all dependencies --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index c380cc6db..a3a5e695d 100644 --- a/setup.py +++ b/setup.py @@ -26,7 +26,7 @@ packages=find_packages(), install_requires=["numpy", "scipy", "matplotlib", "lxml"], extras_require={ - "all": ["pydot", "trimesh", "open3d"], + "all": ["pydot", "trimesh", "pycollada", "open3d"], "doc": ["numpydoc", "sphinx", "sphinx-gallery", "sphinx-bootstrap-theme"], "test": ["pytest", "pytest-cov"] From f44d61561123c7bb7f061cf8a561e392a5710101 Mon Sep 17 00:00:00 2001 From: Alexander Fabisch Date: Thu, 27 Jul 2023 15:12:29 +0200 Subject: [PATCH 06/15] Add docstring --- pytransform3d/mesh_loader.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pytransform3d/mesh_loader.py b/pytransform3d/mesh_loader.py index 1b3cfe59d..77936292f 100644 --- a/pytransform3d/mesh_loader.py +++ b/pytransform3d/mesh_loader.py @@ -6,6 +6,12 @@ def load_mesh(filename, convex_hull=False): """Load mesh from file. + This feature relies on optional dependencies. It can use trimesh or + Open3D to load meshes. If both are not available, it will fail. + Furthermore, some mesh formats require additional dependencies. For + example, loading collada files ('.dae' file ending) requires pycollada + and trimesh. + Parameters ---------- filename : str From 07502b97ff89bfd869fa602f661c339b72e4e3cf Mon Sep 17 00:00:00 2001 From: Alexander Fabisch Date: Thu, 27 Jul 2023 15:17:21 +0200 Subject: [PATCH 07/15] Change loader interface --- pytransform3d/mesh_loader.py | 25 +++++++++------------ pytransform3d/plot_utils/_plot_functions.py | 5 ++++- pytransform3d/visualizer/_artists.py | 5 ++++- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/pytransform3d/mesh_loader.py b/pytransform3d/mesh_loader.py index 77936292f..43c162500 100644 --- a/pytransform3d/mesh_loader.py +++ b/pytransform3d/mesh_loader.py @@ -3,7 +3,7 @@ import numpy as np -def load_mesh(filename, convex_hull=False): +def load_mesh(filename): """Load mesh from file. This feature relies on optional dependencies. It can use trimesh or @@ -17,26 +17,22 @@ def load_mesh(filename, convex_hull=False): filename : str File in which the mesh is stored. - convex_hull : bool, optional (default: False) - Compute convex hull of mesh. - Returns ------- mesh : MeshBase Mesh instance. """ mesh = _Trimesh(filename) - success = mesh.load() + loader_available = mesh.load() - if not success: + if not loader_available: mesh = _Open3DMesh(filename) - success = mesh.load() - - if not success: - raise IOError("Could not load mesh from '%s'" % filename) + loader_available = mesh.load() - if convex_hull: - mesh.convex_hull() + if not loader_available: + raise IOError( + "Could not load mesh from '%s'. Please install one of the " + "optional dependencies 'trimesh' or 'open3d'." % filename) return mesh @@ -58,8 +54,8 @@ def load(self): Returns ------- - success : bool - Has the mesh been loaded? + loader_available : bool + Is the mesh loader available? """ @abc.abstractmethod @@ -97,7 +93,6 @@ def load(self): import trimesh except ImportError: return False - obj = trimesh.load(self.filename) if isinstance(obj, trimesh.Scene): obj = obj.dump().sum() diff --git a/pytransform3d/plot_utils/_plot_functions.py b/pytransform3d/plot_utils/_plot_functions.py index c81f2ca3c..9ce1a5c87 100644 --- a/pytransform3d/plot_utils/_plot_functions.py +++ b/pytransform3d/plot_utils/_plot_functions.py @@ -365,7 +365,10 @@ def plot_mesh(ax=None, filename=None, A2B=np.eye(4), "package directory.") return ax - mesh = load_mesh(filename, convex_hull=convex_hull) + mesh = load_mesh(filename) + if convex_hull: + mesh.convex_hull() + vertices = mesh.vertices * s vertices = np.hstack((vertices, np.ones((len(vertices), 1)))) vertices = transform(A2B, vertices)[:, :3] diff --git a/pytransform3d/visualizer/_artists.py b/pytransform3d/visualizer/_artists.py index b675cfd01..6219d6098 100644 --- a/pytransform3d/visualizer/_artists.py +++ b/pytransform3d/visualizer/_artists.py @@ -555,7 +555,10 @@ class Mesh(Artist): """ def __init__(self, filename, A2B=np.eye(4), s=np.ones(3), c=None, convex_hull=False): - mesh = mesh_loader.load_mesh(filename, convex_hull=convex_hull) + mesh = mesh_loader.load_mesh(filename) + if convex_hull: + mesh.convex_hull() + self.mesh = mesh.get_open3d_mesh() self.mesh.vertices = o3d.utility.Vector3dVector( np.asarray(self.mesh.vertices) * s) From dad23560c088e62c97503b4baa08f10002ddbc7d Mon Sep 17 00:00:00 2001 From: Alexander Fabisch Date: Thu, 27 Jul 2023 15:32:45 +0200 Subject: [PATCH 08/15] Add type stub --- pytransform3d/mesh_loader.pyi | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 pytransform3d/mesh_loader.pyi diff --git a/pytransform3d/mesh_loader.pyi b/pytransform3d/mesh_loader.pyi new file mode 100644 index 000000000..3eab08060 --- /dev/null +++ b/pytransform3d/mesh_loader.pyi @@ -0,0 +1,30 @@ +import abc +from typing import Any + +import numpy.typing as npt + + +class MeshBase(abc.ABC): + filename: str + + def __init__(self, filename: str): ... + + @abc.abstractmethod + def load(self) -> bool: ... + + @abc.abstractmethod + def convex_hull(self): ... + + @abc.abstractmethod + def get_open3d_mesh(self) -> Any: ... + + @property + @abc.abstractmethod + def vertices(self) -> npt.ArrayLike: ... + + @property + @abc.abstractmethod + def triangles(self) -> npt.ArrayLike: ... + + +def load_mesh(filename: str) -> MeshBase: ... From 7147c206b8b52f44e9bb9cdda6ed0c5563be4dea Mon Sep 17 00:00:00 2001 From: Alexander Fabisch Date: Thu, 27 Jul 2023 15:50:10 +0200 Subject: [PATCH 09/15] Test mesh loader --- pytransform3d/mesh_loader.py | 4 +- pytransform3d/test/test_mesh_loader.py | 54 ++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 pytransform3d/test/test_mesh_loader.py diff --git a/pytransform3d/mesh_loader.py b/pytransform3d/mesh_loader.py index 43c162500..bdb8f5371 100644 --- a/pytransform3d/mesh_loader.py +++ b/pytransform3d/mesh_loader.py @@ -29,8 +29,8 @@ def load_mesh(filename): mesh = _Open3DMesh(filename) loader_available = mesh.load() - if not loader_available: - raise IOError( + if not loader_available: # pragma: no cover + raise ImportError( "Could not load mesh from '%s'. Please install one of the " "optional dependencies 'trimesh' or 'open3d'." % filename) diff --git a/pytransform3d/test/test_mesh_loader.py b/pytransform3d/test/test_mesh_loader.py new file mode 100644 index 000000000..59395831d --- /dev/null +++ b/pytransform3d/test/test_mesh_loader.py @@ -0,0 +1,54 @@ +from pytransform3d import mesh_loader + +import pytest + + +def test_trimesh(): + mesh = mesh_loader._Trimesh("test/test_data/cone.stl") + loader_available = mesh.load() + if not loader_available: + pytest.skip("trimesh is required for this test") + + assert len(mesh.vertices) == 64 + assert len(mesh.triangles) == 124 + + mesh.convex_hull() + + assert len(mesh.vertices) == 64 + + +def test_open3d(): + mesh = mesh_loader._Open3DMesh("test/test_data/cone.stl") + loader_available = mesh.load() + if not loader_available: + pytest.skip("open3d is required for this test") + + assert len(mesh.vertices) == 295 + assert len(mesh.triangles) == 124 + + o3d_mesh = mesh.get_open3d_mesh() + assert len(o3d_mesh.vertices) == 295 + + mesh.convex_hull() + + assert len(mesh.vertices) == 64 + + +def test_trimesh_with_open3d(): + mesh = mesh_loader._Trimesh("test/test_data/cone.stl") + loader_available = mesh.load() + if not loader_available: + pytest.skip("trimesh is required for this test") + try: + o3d_mesh = mesh.get_open3d_mesh() + except ImportError: + pytest.skip("open3d is required for this test") + assert len(o3d_mesh.vertices) == 64 + + +def test_interface(): + try: + mesh = mesh_loader.load_mesh("test/test_data/cone.stl") + assert len(mesh.triangles) == 124 + except ImportError: + pytest.skip("trimesh or open3d are required for this test") From 39d8c5a9346445933ded9dec6a6838473636ddd1 Mon Sep 17 00:00:00 2001 From: Alexander Fabisch Date: Thu, 27 Jul 2023 15:56:06 +0200 Subject: [PATCH 10/15] Fix coverage --- pytransform3d/mesh_loader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pytransform3d/mesh_loader.py b/pytransform3d/mesh_loader.py index bdb8f5371..83b4df767 100644 --- a/pytransform3d/mesh_loader.py +++ b/pytransform3d/mesh_loader.py @@ -25,7 +25,7 @@ def load_mesh(filename): mesh = _Trimesh(filename) loader_available = mesh.load() - if not loader_available: + if not loader_available: # pragma: no cover mesh = _Open3DMesh(filename) loader_available = mesh.load() From 75761bc0f2f037286901750d2bd9711dab6ae30a Mon Sep 17 00:00:00 2001 From: Alexander Fabisch Date: Thu, 27 Jul 2023 16:04:05 +0200 Subject: [PATCH 11/15] Increase coverage --- pytransform3d/mesh_loader.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pytransform3d/mesh_loader.py b/pytransform3d/mesh_loader.py index 83b4df767..69dc850e8 100644 --- a/pytransform3d/mesh_loader.py +++ b/pytransform3d/mesh_loader.py @@ -94,7 +94,10 @@ def load(self): except ImportError: return False obj = trimesh.load(self.filename) - if isinstance(obj, trimesh.Scene): + if isinstance(obj, trimesh.Scene): # pragma: no cover + # Special case in which we load a collada file that contains + # multiple meshes. We might lose textures. This is excluded + # from testing as it would add another dependency. obj = obj.dump().sum() self.mesh = obj return True From e52031aeda9bf02776c0e8b1517dcd4d1f26835c Mon Sep 17 00:00:00 2001 From: Alexander Fabisch Date: Thu, 27 Jul 2023 16:06:51 +0200 Subject: [PATCH 12/15] Exclude Open3D mesh loader from coverage --- pytransform3d/mesh_loader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pytransform3d/mesh_loader.py b/pytransform3d/mesh_loader.py index 69dc850e8..42a4c5b16 100644 --- a/pytransform3d/mesh_loader.py +++ b/pytransform3d/mesh_loader.py @@ -120,7 +120,7 @@ def triangles(self): return self.mesh.faces -class _Open3DMesh(MeshBase): +class _Open3DMesh(MeshBase): # pragma: no cover def __init__(self, filename): super(_Open3DMesh, self).__init__(filename) self.mesh = None From 1abe18590b191339c80b32aef0723c18db81883b Mon Sep 17 00:00:00 2001 From: Alexander Fabisch Date: Thu, 27 Jul 2023 16:09:13 +0200 Subject: [PATCH 13/15] Exclude open3d from coverage --- pytransform3d/mesh_loader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pytransform3d/mesh_loader.py b/pytransform3d/mesh_loader.py index 42a4c5b16..834383dbf 100644 --- a/pytransform3d/mesh_loader.py +++ b/pytransform3d/mesh_loader.py @@ -105,7 +105,7 @@ def load(self): def convex_hull(self): self.mesh = self.mesh.convex_hull - def get_open3d_mesh(self): + def get_open3d_mesh(self): # pragma: no cover import open3d return open3d.geometry.TriangleMesh( open3d.utility.Vector3dVector(self.vertices), From 87ae696f87fd9afda57ee4966385a13cd6e5e106 Mon Sep 17 00:00:00 2001 From: Alexander Fabisch Date: Thu, 27 Jul 2023 16:11:22 +0200 Subject: [PATCH 14/15] Add module docstring --- pytransform3d/mesh_loader.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pytransform3d/mesh_loader.py b/pytransform3d/mesh_loader.py index 834383dbf..b42a26330 100644 --- a/pytransform3d/mesh_loader.py +++ b/pytransform3d/mesh_loader.py @@ -1,3 +1,4 @@ +"""Common interface to load meshes.""" import abc import numpy as np From f082bff544c46a29154731b97e3571813f51ab15 Mon Sep 17 00:00:00 2001 From: Alexander Fabisch Date: Fri, 28 Jul 2023 11:20:07 +0200 Subject: [PATCH 15/15] Add pycollada to requirements.txt --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index ba9132165..85caf81c7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,5 +3,6 @@ scipy matplotlib lxml trimesh +pycollada pydot open3d