0
# Analysis and Measurement
1
2
Comprehensive geometric analysis including mass properties, curvature analysis, mesh quality metrics, inertia calculations, and geometric measurements. These tools enable detailed mesh characterization and engineering analysis.
3
4
## Capabilities
5
6
### Mass Properties and Inertia
7
8
Calculate physical properties assuming uniform density.
9
10
```python { .api }
11
@property
12
def mass_properties(self) -> dict:
13
"""
14
Calculate mass properties including center of mass and inertia tensor.
15
16
Returns:
17
dict with keys:
18
- 'volume': float, mesh volume
19
- 'mass': float, mass (volume * density)
20
- 'density': float, material density
21
- 'center_mass': (3,) center of mass
22
- 'inertia': (3, 3) inertia tensor
23
"""
24
25
@property
26
def moment_inertia(self) -> np.ndarray:
27
"""
28
Moment of inertia tensor around center of mass.
29
30
Returns:
31
(3, 3) inertia tensor matrix
32
"""
33
34
@property
35
def principal_inertia_components(self) -> np.ndarray:
36
"""
37
Principal moments of inertia (eigenvalues of inertia tensor).
38
39
Returns:
40
(3,) principal inertia values in ascending order
41
"""
42
43
@property
44
def principal_inertia_vectors(self) -> np.ndarray:
45
"""
46
Principal inertia axes (eigenvectors of inertia tensor).
47
48
Returns:
49
(3, 3) matrix where columns are principal axes
50
"""
51
52
@property
53
def center_mass(self) -> np.ndarray:
54
"""
55
Center of mass (volume-weighted centroid).
56
57
Returns:
58
(3,) center of mass coordinates
59
"""
60
```
61
62
### Surface Curvature Analysis
63
64
Analyze mesh curvature properties and surface characteristics.
65
66
```python { .api }
67
def discrete_gaussian_curvature_measure(self) -> np.ndarray:
68
"""
69
Discrete Gaussian curvature at each vertex.
70
71
Returns:
72
(n,) Gaussian curvature values at vertices
73
"""
74
75
def discrete_mean_curvature_measure(self) -> np.ndarray:
76
"""
77
Discrete mean curvature at each vertex.
78
79
Returns:
80
(n,) mean curvature values at vertices
81
"""
82
83
def vertex_defects(self) -> np.ndarray:
84
"""
85
Angular defect at each vertex (2π - sum of incident angles).
86
87
Returns:
88
(n,) angular defects at vertices
89
"""
90
91
def face_angles(self) -> np.ndarray:
92
"""
93
Interior angles of each face.
94
95
Returns:
96
(m, 3) angles for each face corner
97
"""
98
99
def face_angles_sparse(self) -> np.ndarray:
100
"""
101
Sparse representation of face angles.
102
103
Returns:
104
(3*m,) flattened face angles
105
"""
106
```
107
108
### Geometric Measurements
109
110
Basic geometric properties and measurements.
111
112
```python { .api }
113
@property
114
def area(self) -> float:
115
"""Total surface area"""
116
117
@property
118
def area_faces(self) -> np.ndarray:
119
"""
120
Surface area of each face.
121
122
Returns:
123
(m,) area values for each face
124
"""
125
126
@property
127
def volume(self) -> float:
128
"""
129
Mesh volume (positive for watertight meshes).
130
131
Returns:
132
float, volume value
133
"""
134
135
@property
136
def bounds(self) -> np.ndarray:
137
"""
138
Axis-aligned bounding box.
139
140
Returns:
141
(2, 3) array: [[min_x, min_y, min_z], [max_x, max_y, max_z]]
142
"""
143
144
@property
145
def extents(self) -> np.ndarray:
146
"""
147
Size in each dimension.
148
149
Returns:
150
(3,) array: [width, height, depth]
151
"""
152
153
@property
154
def scale(self) -> float:
155
"""Scaling factor relative to unit cube"""
156
157
@property
158
def centroid(self) -> np.ndarray:
159
"""
160
Geometric centroid (average of vertices).
161
162
Returns:
163
(3,) centroid coordinates
164
"""
165
```
166
167
### Mesh Quality Metrics
168
169
Analyze mesh quality and identify potential issues.
170
171
```python { .api }
172
@property
173
def euler_number(self) -> int:
174
"""
175
Euler characteristic (V - E + F).
176
177
Returns:
178
int, Euler number (2 for closed surfaces)
179
"""
180
181
@property
182
def is_watertight(self) -> bool:
183
"""True if mesh is watertight (manifold and closed)"""
184
185
@property
186
def is_winding_consistent(self) -> bool:
187
"""True if face winding is consistent"""
188
189
@property
190
def is_volume(self) -> bool:
191
"""True if mesh encloses a volume"""
192
193
@property
194
def face_adjacency(self) -> np.ndarray:
195
"""
196
Face adjacency matrix.
197
198
Returns:
199
(m, m) sparse matrix showing face connectivity
200
"""
201
202
@property
203
def face_adjacency_edges(self) -> np.ndarray:
204
"""
205
Edges between adjacent faces.
206
207
Returns:
208
(p, 2) array of face indices sharing edges
209
"""
210
211
def face_adjacency_convex(self, tolerance=0.0) -> np.ndarray:
212
"""
213
Check if adjacent faces form convex angles.
214
215
Parameters:
216
- tolerance: float, angle tolerance
217
218
Returns:
219
(p,) boolean array indicating convex adjacencies
220
"""
221
222
def face_normals_from_vertices(self) -> np.ndarray:
223
"""
224
Calculate face normals from vertex positions.
225
226
Returns:
227
(m, 3) face normal vectors
228
"""
229
```
230
231
### Statistical Analysis
232
233
Compute statistical properties of mesh geometry.
234
235
```python { .api }
236
def vertex_degree(self) -> np.ndarray:
237
"""
238
Number of faces incident to each vertex.
239
240
Returns:
241
(n,) vertex degrees
242
"""
243
244
def edge_lengths(self) -> np.ndarray:
245
"""
246
Length of each unique edge.
247
248
Returns:
249
(e,) edge length values
250
"""
251
252
def edge_lengths_histogram(self, bins=20) -> tuple:
253
"""
254
Histogram of edge lengths.
255
256
Parameters:
257
- bins: int, number of histogram bins
258
259
Returns:
260
tuple: (counts, bin_edges)
261
"""
262
263
def face_areas_histogram(self, bins=20) -> tuple:
264
"""
265
Histogram of face areas.
266
267
Parameters:
268
- bins: int, number of histogram bins
269
270
Returns:
271
tuple: (counts, bin_edges)
272
"""
273
```
274
275
### Mesh Comparison and Metrics
276
277
Compare meshes and compute similarity metrics.
278
279
```python { .api }
280
def hausdorff_distance(self, other) -> float:
281
"""
282
Hausdorff distance to another mesh.
283
284
Parameters:
285
- other: Trimesh object
286
287
Returns:
288
float, maximum distance between meshes
289
"""
290
291
def symmetric_difference_volume(self, other) -> float:
292
"""
293
Volume of symmetric difference with another mesh.
294
295
Parameters:
296
- other: Trimesh object
297
298
Returns:
299
float, volume of non-overlapping regions
300
"""
301
302
def difference_volume(self, other) -> float:
303
"""
304
Volume difference with another mesh.
305
306
Parameters:
307
- other: Trimesh object
308
309
Returns:
310
float, volume difference
311
"""
312
```
313
314
### Advanced Geometric Analysis
315
316
Specialized geometric analysis functions.
317
318
```python { .api }
319
def integral_gaussian_curvature(self) -> float:
320
"""
321
Total Gaussian curvature (should equal 4π for closed surfaces).
322
323
Returns:
324
float, integrated Gaussian curvature
325
"""
326
327
def integral_mean_curvature(self) -> float:
328
"""
329
Total mean curvature over surface.
330
331
Returns:
332
float, integrated mean curvature
333
"""
334
335
def sphericity(self) -> float:
336
"""
337
Sphericity measure (how sphere-like the mesh is).
338
339
Returns:
340
float, sphericity value (1.0 for perfect sphere)
341
"""
342
343
def compactness(self) -> float:
344
"""
345
Compactness measure (surface area vs volume ratio).
346
347
Returns:
348
float, compactness value
349
"""
350
```
351
352
## Usage Examples
353
354
### Mass Properties Analysis
355
356
```python
357
import trimesh
358
import numpy as np
359
360
# Load mesh
361
mesh = trimesh.load('part.stl')
362
363
# Set material density (kg/m³)
364
mesh.density = 7850 # Steel density
365
366
# Get complete mass properties
367
props = mesh.mass_properties
368
print(f"Volume: {props['volume']:.6f} m³")
369
print(f"Mass: {props['mass']:.3f} kg")
370
print(f"Center of mass: {props['center_mass']}")
371
print(f"Inertia tensor:\n{props['inertia']}")
372
373
# Principal inertia analysis
374
principal_values = mesh.principal_inertia_components
375
principal_vectors = mesh.principal_inertia_vectors
376
377
print(f"Principal moments: {principal_values}")
378
print(f"Principal axes:\n{principal_vectors}")
379
380
# Transform to principal axes
381
transform = mesh.principal_inertia_transform
382
aligned_mesh = mesh.copy()
383
aligned_mesh.apply_transform(transform)
384
```
385
386
### Curvature Analysis
387
388
```python
389
# Calculate curvature properties
390
gaussian_curvature = mesh.discrete_gaussian_curvature_measure()
391
mean_curvature = mesh.discrete_mean_curvature_measure()
392
393
# Analyze curvature distribution
394
print(f"Gaussian curvature range: {gaussian_curvature.min():.4f} to {gaussian_curvature.max():.4f}")
395
print(f"Mean curvature range: {mean_curvature.min():.4f} to {mean_curvature.max():.4f}")
396
397
# Find high curvature regions
398
high_curvature_threshold = np.percentile(np.abs(mean_curvature), 90)
399
high_curvature_vertices = np.where(np.abs(mean_curvature) > high_curvature_threshold)[0]
400
401
print(f"High curvature vertices: {len(high_curvature_vertices)}")
402
403
# Visualize curvature
404
if mesh.visual.kind == 'vertex':
405
# Color vertices by curvature
406
curvature_normalized = (mean_curvature - mean_curvature.min()) / (mean_curvature.max() - mean_curvature.min())
407
colors = plt.cm.viridis(curvature_normalized)[:, :3] * 255
408
mesh.visual.vertex_colors = colors.astype(np.uint8)
409
mesh.show()
410
```
411
412
### Mesh Quality Assessment
413
414
```python
415
# Basic quality checks
416
print(f"Is watertight: {mesh.is_watertight}")
417
print(f"Is winding consistent: {mesh.is_winding_consistent}")
418
print(f"Euler number: {mesh.euler_number}")
419
print(f"Vertex count: {len(mesh.vertices)}")
420
print(f"Face count: {len(mesh.faces)}")
421
422
# Geometric properties
423
print(f"Volume: {mesh.volume:.6f}")
424
print(f"Surface area: {mesh.area:.6f}")
425
print(f"Bounds: {mesh.bounds}")
426
print(f"Extents: {mesh.extents}")
427
428
# Quality metrics
429
print(f"Sphericity: {mesh.sphericity():.4f}")
430
print(f"Compactness: {mesh.compactness():.4f}")
431
432
# Edge and face statistics
433
edge_lengths = mesh.edge_lengths()
434
face_areas = mesh.area_faces
435
436
print(f"Edge length range: {edge_lengths.min():.6f} to {edge_lengths.max():.6f}")
437
print(f"Face area range: {face_areas.min():.6f} to {face_areas.max():.6f}")
438
print(f"Mean edge length: {edge_lengths.mean():.6f}")
439
print(f"Mean face area: {face_areas.mean():.6f}")
440
```
441
442
### Mesh Comparison
443
444
```python
445
# Load two meshes to compare
446
mesh1 = trimesh.load('original.stl')
447
mesh2 = trimesh.load('modified.stl')
448
449
# Hausdorff distance (measure of maximum deviation)
450
hausdorff_dist = mesh1.hausdorff_distance(mesh2)
451
print(f"Hausdorff distance: {hausdorff_dist:.6f}")
452
453
# Volume comparison
454
vol_diff = abs(mesh1.volume - mesh2.volume)
455
vol_percent = (vol_diff / mesh1.volume) * 100
456
print(f"Volume difference: {vol_diff:.6f} ({vol_percent:.2f}%)")
457
458
# Symmetric difference volume
459
sym_diff_vol = mesh1.symmetric_difference_volume(mesh2)
460
print(f"Symmetric difference volume: {sym_diff_vol:.6f}")
461
462
# Surface area comparison
463
area_diff = abs(mesh1.area - mesh2.area)
464
area_percent = (area_diff / mesh1.area) * 100
465
print(f"Surface area difference: {area_diff:.6f} ({area_percent:.2f}%)")
466
```
467
468
### Statistical Analysis
469
470
```python
471
import matplotlib.pyplot as plt
472
473
# Vertex degree distribution
474
vertex_degrees = mesh.vertex_degree()
475
print(f"Vertex degree range: {vertex_degrees.min()} to {vertex_degrees.max()}")
476
print(f"Mean vertex degree: {vertex_degrees.mean():.2f}")
477
478
# Edge length histogram
479
edge_lengths = mesh.edge_lengths()
480
counts, bin_edges = mesh.edge_lengths_histogram(bins=50)
481
482
plt.figure(figsize=(10, 6))
483
plt.subplot(1, 2, 1)
484
plt.hist(edge_lengths, bins=50, alpha=0.7)
485
plt.xlabel('Edge Length')
486
plt.ylabel('Count')
487
plt.title('Edge Length Distribution')
488
489
# Face area histogram
490
face_areas = mesh.area_faces
491
plt.subplot(1, 2, 2)
492
plt.hist(face_areas, bins=50, alpha=0.7)
493
plt.xlabel('Face Area')
494
plt.ylabel('Count')
495
plt.title('Face Area Distribution')
496
497
plt.tight_layout()
498
plt.show()
499
500
# Find outliers
501
edge_mean = edge_lengths.mean()
502
edge_std = edge_lengths.std()
503
edge_outliers = np.where(edge_lengths > edge_mean + 3*edge_std)[0]
504
print(f"Edge length outliers: {len(edge_outliers)} edges")
505
```