/
parent.py
203 lines (170 loc) · 5.83 KB
/
parent.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
"""
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
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)
@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