230 lines
6.5 KiB
Python
230 lines
6.5 KiB
Python
|
"""
|
||
|
parent.py
|
||
|
-------------
|
||
|
|
||
|
The base class for Trimesh, PointCloud, and Scene objects
|
||
|
"""
|
||
|
import abc
|
||
|
|
||
|
import numpy as np
|
||
|
|
||
|
from . import caching
|
||
|
from . import transformations
|
||
|
from .util import ABC
|
||
|
|
||
|
|
||
|
class Geometry(ABC):
|
||
|
|
||
|
"""Parent of geometry classes.
|
||
|
|
||
|
The `Geometry` object is the parent object of geometry classes, including:
|
||
|
Trimesh, PointCloud, and Scene objects.
|
||
|
|
||
|
By decorating a method with `abc.abstractmethod` it just means the objects
|
||
|
that inherit from `Geometry` MUST implement those methods.
|
||
|
"""
|
||
|
|
||
|
@abc.abstractproperty
|
||
|
def bounds(self):
|
||
|
pass
|
||
|
|
||
|
@abc.abstractproperty
|
||
|
def extents(self):
|
||
|
pass
|
||
|
|
||
|
@abc.abstractmethod
|
||
|
def apply_transform(self, matrix):
|
||
|
pass
|
||
|
|
||
|
@abc.abstractmethod
|
||
|
def is_empty(self):
|
||
|
pass
|
||
|
|
||
|
@abc.abstractmethod
|
||
|
def crc(self):
|
||
|
pass
|
||
|
|
||
|
@abc.abstractmethod
|
||
|
def md5(self):
|
||
|
pass
|
||
|
|
||
|
def __repr__(self):
|
||
|
"""
|
||
|
Print quick summary of the current geometry without
|
||
|
computing properties.
|
||
|
"""
|
||
|
elements = []
|
||
|
if hasattr(self, 'vertices'):
|
||
|
# for Trimesh and PointCloud
|
||
|
elements.append('vertices.shape={}'.format(
|
||
|
self.vertices.shape))
|
||
|
if hasattr(self, 'faces'):
|
||
|
# for Trimesh
|
||
|
elements.append('faces.shape={}'.format(
|
||
|
self.faces.shape))
|
||
|
if hasattr(self, 'geometry') and isinstance(
|
||
|
self.geometry, dict):
|
||
|
# for Scene
|
||
|
elements.append('len(geometry)={}'.format(
|
||
|
len(self.geometry)))
|
||
|
if 'Voxel' in type(self).__name__:
|
||
|
# for VoxelGrid objects
|
||
|
elements.append(str(self.shape)[1:-1])
|
||
|
return '<trimesh.{}({})>'.format(
|
||
|
type(self).__name__, ', '.join(elements))
|
||
|
|
||
|
def apply_translation(self, translation):
|
||
|
"""
|
||
|
Translate the current mesh.
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
translation : (3,) float
|
||
|
Translation in XYZ
|
||
|
"""
|
||
|
translation = np.asanyarray(translation, dtype=np.float64)
|
||
|
if translation.shape != (3,):
|
||
|
raise ValueError('Translation must be (3,)!')
|
||
|
|
||
|
matrix = np.eye(4)
|
||
|
matrix[:3, 3] = translation
|
||
|
return self.apply_transform(matrix)
|
||
|
|
||
|
def apply_scale(self, scaling):
|
||
|
"""
|
||
|
Scale the mesh.
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
scaling : float or (3,) float
|
||
|
Scale factor to apply to the mesh
|
||
|
"""
|
||
|
matrix = transformations.scale_and_translate(scale=scaling)
|
||
|
# apply_transform will work nicely even on negative scales
|
||
|
return self.apply_transform(matrix)
|
||
|
|
||
|
def apply_obb(self):
|
||
|
"""
|
||
|
Apply the oriented bounding box transform to the current mesh.
|
||
|
|
||
|
This will result in a mesh with an AABB centered at the
|
||
|
origin and the same dimensions as the OBB.
|
||
|
|
||
|
Returns
|
||
|
----------
|
||
|
matrix : (4, 4) float
|
||
|
Transformation matrix that was applied
|
||
|
to mesh to move it into OBB frame
|
||
|
"""
|
||
|
matrix = self.bounding_box_oriented.primitive.transform
|
||
|
matrix = np.linalg.inv(matrix)
|
||
|
self.apply_transform(matrix)
|
||
|
return matrix
|
||
|
|
||
|
@abc.abstractmethod
|
||
|
def copy(self):
|
||
|
pass
|
||
|
|
||
|
@abc.abstractmethod
|
||
|
def show(self):
|
||
|
pass
|
||
|
|
||
|
@caching.cache_decorator
|
||
|
def bounding_box(self):
|
||
|
"""
|
||
|
An axis aligned bounding box for the current mesh.
|
||
|
|
||
|
Returns
|
||
|
----------
|
||
|
aabb : trimesh.primitives.Box
|
||
|
Box object with transform and extents defined
|
||
|
representing the axis aligned bounding box of the mesh
|
||
|
"""
|
||
|
from . import primitives
|
||
|
|
||
|
transform = np.eye(4)
|
||
|
# translate to center of axis aligned bounds
|
||
|
transform[:3, 3] = self.bounds.mean(axis=0)
|
||
|
|
||
|
aabb = primitives.Box(transform=transform,
|
||
|
extents=self.extents,
|
||
|
mutable=False)
|
||
|
return aabb
|
||
|
|
||
|
@caching.cache_decorator
|
||
|
def bounding_box_oriented(self):
|
||
|
"""
|
||
|
An oriented bounding box for the current mesh.
|
||
|
|
||
|
Returns
|
||
|
---------
|
||
|
obb : trimesh.primitives.Box
|
||
|
Box object with transform and extents defined
|
||
|
representing the minimum volume oriented
|
||
|
bounding box of the mesh
|
||
|
"""
|
||
|
from . import primitives, bounds
|
||
|
to_origin, extents = bounds.oriented_bounds(self)
|
||
|
obb = primitives.Box(transform=np.linalg.inv(to_origin),
|
||
|
extents=extents,
|
||
|
mutable=False)
|
||
|
return obb
|
||
|
|
||
|
@caching.cache_decorator
|
||
|
def bounding_sphere(self):
|
||
|
"""
|
||
|
A minimum volume bounding sphere for the current mesh.
|
||
|
|
||
|
Note that the Sphere primitive returned has an unpadded, exact
|
||
|
sphere_radius so while the distance of every vertex of the current
|
||
|
mesh from sphere_center will be less than sphere_radius, the faceted
|
||
|
sphere primitive may not contain every vertex
|
||
|
|
||
|
Returns
|
||
|
--------
|
||
|
minball : trimesh.primitives.Sphere
|
||
|
Sphere primitive containing current mesh
|
||
|
"""
|
||
|
from . import primitives, nsphere
|
||
|
center, radius = nsphere.minimum_nsphere(self)
|
||
|
minball = primitives.Sphere(center=center,
|
||
|
radius=radius,
|
||
|
mutable=False)
|
||
|
return minball
|
||
|
|
||
|
@caching.cache_decorator
|
||
|
def bounding_cylinder(self):
|
||
|
"""
|
||
|
A minimum volume bounding cylinder for the current mesh.
|
||
|
|
||
|
Returns
|
||
|
--------
|
||
|
mincyl : trimesh.primitives.Cylinder
|
||
|
Cylinder primitive containing current mesh
|
||
|
"""
|
||
|
from . import primitives, bounds
|
||
|
kwargs = bounds.minimum_cylinder(self)
|
||
|
mincyl = primitives.Cylinder(mutable=False, **kwargs)
|
||
|
return mincyl
|
||
|
|
||
|
@caching.cache_decorator
|
||
|
def bounding_primitive(self):
|
||
|
"""
|
||
|
The minimum volume primitive (box, sphere, or cylinder) that
|
||
|
bounds the mesh.
|
||
|
|
||
|
Returns
|
||
|
---------
|
||
|
bounding_primitive : object
|
||
|
Smallest primitive which bounds the mesh:
|
||
|
trimesh.primitives.Sphere
|
||
|
trimesh.primitives.Box
|
||
|
trimesh.primitives.Cylinder
|
||
|
"""
|
||
|
options = [self.bounding_box_oriented,
|
||
|
self.bounding_sphere,
|
||
|
self.bounding_cylinder]
|
||
|
volume_min = np.argmin([i.volume for i in options])
|
||
|
bounding_primitive = options[volume_min]
|
||
|
return bounding_primitive
|