from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import os
from compas.datastructures import Mesh
__all__ = [
'AbstractMeshLoader',
'DefaultMeshLoader',
'LocalPackageMeshLoader'
]
try:
from urllib.parse import urlparse
except ImportError:
from urlparse import urlparse
SUPPORTED_FORMATS = ('obj', 'stl', 'ply')
[docs]class AbstractMeshLoader(object):
"""Basic contract/interface for all mesh loaders."""
[docs] def can_load_mesh(self, url):
"""Determine whether this loader can load a given Mesh URL.
Parameters
----------
url : str
Mesh URL.
Returns
-------
bool
``True`` if it can handle it, otherwise ``False``.
"""
return NotImplementedError
[docs] def load_mesh(self, url):
"""Load the mesh from the given URL.
Parameters
----------
url : str
Mesh URL
Returns
-------
:class:`Mesh`
Instance of a mesh.
"""
return NotImplementedError
[docs]class DefaultMeshLoader(AbstractMeshLoader):
"""Handles basic mesh loader tasks, mostly from local files.
Attributes
----------
kwargs (optional): dict
Additional keyword arguments.
"""
[docs] def __init__(self, **kwargs):
super(DefaultMeshLoader, self).__init__()
self.attr = kwargs or dict()
[docs] def can_load_mesh(self, url):
"""Determine whether this loader can load a given mesh URL.
Parameters
----------
url : str
Mesh URL.
Returns
-------
bool
``True`` if the URL points to a local and valid file.
Otherwise ``False``.
"""
url = self._get_mesh_url(url)
scheme = urlparse(url).scheme
# Local files have either:
# - no scheme
# - a one-letter scheme in Windows
# - file scheme
is_local_file = len(scheme) in (0, 1) or scheme == 'file'
if is_local_file:
if os.path.isfile(url):
return True
# Only OBJ loader supports remote files atm
is_obj = _get_file_format(url) == 'obj'
return scheme in ('http', 'https') and is_obj
[docs] def load_mesh(self, url):
"""Loads a mesh from local storage.
Parameters
----------
url : str
Mesh location
Returns
-------
:class:`Mesh`
Instance of a mesh.
"""
url = self._get_mesh_url(url)
return _mesh_import(url, url)
def _get_mesh_url(self, url):
"""Concatenates basepath directory to URL only if defined in the keyword arguments.
It also strips out the scheme 'file:///' from the URL if present.
Parameters
----------
url : str
Mesh location.
Returns
-------
url: str
Extended mesh url location if basepath in kwargs.
Else, it returns url.
"""
if url.startswith('file:///'):
url = url[8:]
basepath = self.attr.get('basepath')
if basepath:
return os.path.join(basepath, url)
return url
def _get_file_format(url):
# This could be much more elaborate
# with an actual header check
# and/or remote content-type check
file_extension = url.split('.')[-1].lower()
return file_extension
[docs]class LocalPackageMeshLoader(AbstractMeshLoader):
"""Loads suport package resources stored locally.
Attributes
----------
path : str
Path where the package is stored locally.
support_package : str
Name of the support package containing URDF, Meshes
and additional assets, e.g. 'abb_irb4400_support'
"""
[docs] def __init__(self, path, support_package):
super(LocalPackageMeshLoader, self).__init__()
self.path = path
self.support_package = support_package
self.schema_prefix = 'package://' + self.support_package + '/'
[docs] def build_path(self, *path_parts):
"""Returns the building path.
Parameters
----------
*path_parts: str
The additional foldernames that construct the path.
Returns
-------
str
"""
return os.path.join(self.path,
self.support_package,
*path_parts)
[docs] def load_urdf(self, file):
"""Load a URDF file from local storage.
Parameters
----------
file : str
File name. Following convention, the file should reside
inside a ``urdf`` folder.
"""
path = self.build_path('urdf', file)
return open(path, 'r')
[docs] def can_load_mesh(self, url):
"""Determine whether this loader can load a given mesh URL.
Parameters
----------
url : str
Mesh URL.
Returns
-------
bool
``True`` if the URL uses the ``package://` scheme and the package name
matches the specified in the constructor and the file exists locally,
otherwise ``False``.
"""
if not url.startswith(self.schema_prefix):
return False
local_file = self._get_local_path(url)
return os.path.isfile(local_file)
[docs] def load_mesh(self, url):
"""Loads a mesh from local storage.
Parameters
----------
url : str
Mesh location
Returns
-------
:class:`Mesh`
Instance of a mesh.
"""
local_file = self._get_local_path(url)
return _mesh_import(url, local_file)
def _get_local_path(self, url):
_prefix, path = url.split(self.schema_prefix)
return self.build_path(*path.split('/'))
def _mesh_import(name, file):
"""Internal function to load meshes using the correct loader.
Name and file might be the same but not always, e.g. temp files."""
file_extension = _get_file_format(name)
if file_extension not in SUPPORTED_FORMATS:
raise NotImplementedError(
'Mesh type not supported: {}'.format(file_extension))
if file_extension == 'obj':
return Mesh.from_obj(file)
elif file_extension == 'stl':
return Mesh.from_stl(file)
elif file_extension == 'ply':
return Mesh.from_ply(file)
raise Exception