0
# Spatial Queries and Collision
1
2
Ray casting, nearest point queries, collision detection, and spatial acceleration structures for efficient geometric queries. These capabilities enable intersection testing, proximity analysis, and collision detection workflows.
3
4
## Capabilities
5
6
### Ray Casting and Intersections
7
8
Cast rays against mesh geometry to find intersections.
9
10
```python { .api }
11
def ray_triangle(self, ray_origins, ray_directions, multiple_hits=True, **kwargs):
12
"""
13
Intersect rays with mesh triangles.
14
15
Parameters:
16
- ray_origins: (n, 3) ray starting points
17
- ray_directions: (n, 3) ray direction vectors
18
- multiple_hits: bool, return all intersections or just first
19
- **kwargs: additional ray casting options
20
21
Returns:
22
dict with keys:
23
- 'locations': (h, 3) intersection points
24
- 'index_ray': (h,) ray indices for each hit
25
- 'index_tri': (h,) triangle indices for each hit
26
- 't': (h,) parametric distance along rays
27
"""
28
29
def ray_pyembree(self, ray_origins, ray_directions, **kwargs):
30
"""
31
High-performance ray casting using Intel Embree.
32
33
Note: Requires pyembree optional dependency
34
35
Parameters:
36
- ray_origins: (n, 3) ray starting points
37
- ray_directions: (n, 3) ray direction vectors
38
- **kwargs: Embree-specific options
39
40
Returns:
41
Ray intersection results
42
"""
43
44
def intersects_ray(self, ray_origins, ray_directions) -> np.ndarray:
45
"""
46
Check if rays intersect mesh (boolean test).
47
48
Parameters:
49
- ray_origins: (n, 3) ray starting points
50
- ray_directions: (n, 3) ray direction vectors
51
52
Returns:
53
(n,) boolean array indicating intersections
54
"""
55
```
56
57
### Nearest Point Queries
58
59
Find nearest points, vertices, and faces on mesh surfaces.
60
61
```python { .api }
62
@property
63
def nearest(self):
64
"""Nearest point query interface"""
65
66
def nearest_point(self, points) -> tuple:
67
"""
68
Find nearest points on mesh surface.
69
70
Parameters:
71
- points: (n, 3) query points
72
73
Returns:
74
tuple: (closest_points, distances, triangle_ids)
75
- closest_points: (n, 3) nearest surface points
76
- distances: (n,) distances to surface
77
- triangle_ids: (n,) triangle indices of closest points
78
"""
79
80
def nearest_vertex(self, points) -> tuple:
81
"""
82
Find nearest mesh vertices.
83
84
Parameters:
85
- points: (n, 3) query points
86
87
Returns:
88
tuple: (vertex_ids, distances)
89
- vertex_ids: (n,) indices of nearest vertices
90
- distances: (n,) distances to nearest vertices
91
"""
92
93
def nearest_face(self, points) -> tuple:
94
"""
95
Find nearest face centers.
96
97
Parameters:
98
- points: (n, 3) query points
99
100
Returns:
101
tuple: (face_ids, distances)
102
- face_ids: (n,) indices of nearest faces
103
- distances: (n,) distances to face centers
104
"""
105
```
106
107
### Point Containment Testing
108
109
Test if points are inside or outside mesh volumes.
110
111
```python { .api }
112
def contains(self, points) -> np.ndarray:
113
"""
114
Test if points are inside the mesh volume.
115
116
Parameters:
117
- points: (n, 3) points to test
118
119
Returns:
120
(n,) boolean array, True for points inside mesh
121
"""
122
123
def ray_cast_contains(self, points) -> np.ndarray:
124
"""
125
Point-in-mesh testing using ray casting.
126
127
Parameters:
128
- points: (n, 3) points to test
129
130
Returns:
131
(n,) boolean array indicating containment
132
"""
133
134
def signed_distance(self, points) -> np.ndarray:
135
"""
136
Signed distance to mesh surface.
137
138
Parameters:
139
- points: (n, 3) query points
140
141
Returns:
142
(n,) signed distances (negative inside, positive outside)
143
"""
144
```
145
146
### Collision Detection
147
148
Detect collisions and compute contact information between meshes.
149
150
```python { .api }
151
def collision(self, other_mesh) -> dict:
152
"""
153
Check collision with another mesh.
154
155
Parameters:
156
- other_mesh: Trimesh object to test collision with
157
158
Returns:
159
dict with collision information:
160
- 'collision': bool, True if meshes collide
161
- 'contact_points': (n, 3) contact points
162
- 'contact_normals': (n, 3) contact normal vectors
163
- 'penetration_depth': float, maximum penetration
164
"""
165
166
def is_collision(self, other_mesh) -> bool:
167
"""
168
Fast boolean collision test.
169
170
Parameters:
171
- other_mesh: Trimesh object
172
173
Returns:
174
bool, True if meshes collide
175
"""
176
177
def collision_manager(self, other_meshes) -> dict:
178
"""
179
Manage collisions with multiple meshes.
180
181
Parameters:
182
- other_meshes: list of Trimesh objects
183
184
Returns:
185
dict mapping mesh pairs to collision results
186
"""
187
```
188
189
### Mesh Intersection and Overlap
190
191
Compute intersections and overlapping regions between meshes.
192
193
```python { .api }
194
def intersection_sphere(self, sphere_center, sphere_radius) -> 'Trimesh':
195
"""
196
Intersect mesh with sphere.
197
198
Parameters:
199
- sphere_center: (3,) center of sphere
200
- sphere_radius: float, sphere radius
201
202
Returns:
203
Trimesh containing intersection region
204
"""
205
206
def intersection_cylinder(self, cylinder_center, cylinder_axis, cylinder_radius, cylinder_height) -> 'Trimesh':
207
"""
208
Intersect mesh with cylinder.
209
210
Parameters:
211
- cylinder_center: (3,) center of cylinder
212
- cylinder_axis: (3,) cylinder axis direction
213
- cylinder_radius: float, cylinder radius
214
- cylinder_height: float, cylinder height
215
216
Returns:
217
Trimesh containing intersection region
218
"""
219
220
def overlap_volume(self, other_mesh) -> float:
221
"""
222
Volume of overlap with another mesh.
223
224
Parameters:
225
- other_mesh: Trimesh object
226
227
Returns:
228
float, overlapping volume
229
"""
230
```
231
232
### Spatial Acceleration Structures
233
234
Access and configure spatial acceleration for faster queries.
235
236
```python { .api }
237
@property
238
def kdtree(self):
239
"""KD-tree for vertex spatial queries"""
240
241
@property
242
def rtree(self):
243
"""R-tree for face bounding box queries"""
244
245
def build_kdtree(self) -> None:
246
"""Build KD-tree acceleration structure"""
247
248
def build_rtree(self) -> None:
249
"""Build R-tree acceleration structure"""
250
251
def query_ball_point(self, points, radius) -> list:
252
"""
253
Find all vertices within radius of query points.
254
255
Parameters:
256
- points: (n, 3) query points
257
- radius: float, search radius
258
259
Returns:
260
list of vertex index arrays for each query point
261
"""
262
263
def query_ball_tree(self, other_tree, radius) -> list:
264
"""
265
Find all vertex pairs within radius between meshes.
266
267
Parameters:
268
- other_tree: KD-tree of another mesh
269
- radius: float, search radius
270
271
Returns:
272
list of vertex index pairs
273
"""
274
```
275
276
### Distance and Proximity Analysis
277
278
Compute various distance metrics and proximity measures.
279
280
```python { .api }
281
def distance_to_mesh(self, other_mesh) -> dict:
282
"""
283
Comprehensive distance analysis to another mesh.
284
285
Parameters:
286
- other_mesh: Trimesh object
287
288
Returns:
289
dict with distance metrics:
290
- 'mean_distance': float, mean surface distance
291
- 'rms_distance': float, RMS distance
292
- 'hausdorff_distance': float, maximum distance
293
- 'min_distance': float, minimum distance
294
"""
295
296
def closest_point_cloud(self, point_cloud) -> tuple:
297
"""
298
Find closest points on mesh to point cloud.
299
300
Parameters:
301
- point_cloud: (n, 3) point coordinates
302
303
Returns:
304
tuple: (closest_points, distances, face_indices)
305
"""
306
307
def vertex_adjacency_graph(self) -> dict:
308
"""
309
Build vertex adjacency graph for mesh traversal.
310
311
Returns:
312
dict mapping vertex indices to adjacent vertex lists
313
"""
314
```
315
316
## Usage Examples
317
318
### Ray Casting
319
320
```python
321
import trimesh
322
import numpy as np
323
324
# Load mesh
325
mesh = trimesh.load('model.stl')
326
327
# Define rays (shooting downward from above)
328
ray_origins = np.array([
329
[0, 0, 5],
330
[1, 1, 5],
331
[-1, -1, 5]
332
])
333
ray_directions = np.array([
334
[0, 0, -1],
335
[0, 0, -1],
336
[0, 0, -1]
337
])
338
339
# Cast rays
340
intersections = mesh.ray_triangle(ray_origins, ray_directions)
341
342
print(f"Found {len(intersections['locations'])} intersections")
343
print(f"Intersection points:\n{intersections['locations']}")
344
print(f"Triangle indices: {intersections['index_tri']}")
345
print(f"Ray parameters: {intersections['t']}")
346
347
# Check if rays hit (boolean test)
348
hits = mesh.intersects_ray(ray_origins, ray_directions)
349
print(f"Ray hits: {hits}")
350
```
351
352
### Nearest Point Queries
353
354
```python
355
# Define query points
356
query_points = np.random.uniform(-2, 2, (100, 3))
357
358
# Find nearest points on surface
359
closest_points, distances, triangle_ids = mesh.nearest_point(query_points)
360
361
print(f"Mean distance to surface: {distances.mean():.4f}")
362
print(f"Max distance to surface: {distances.max():.4f}")
363
print(f"Min distance to surface: {distances.min():.4f}")
364
365
# Find nearest vertices
366
vertex_ids, vertex_distances = mesh.nearest_vertex(query_points)
367
print(f"Mean distance to nearest vertex: {vertex_distances.mean():.4f}")
368
369
# Visualize results
370
import matplotlib.pyplot as plt
371
from mpl_toolkits.mplot3d import Axes3D
372
373
fig = plt.figure(figsize=(12, 5))
374
375
# Plot distance histogram
376
ax1 = fig.add_subplot(121)
377
ax1.hist(distances, bins=20, alpha=0.7)
378
ax1.set_xlabel('Distance to Surface')
379
ax1.set_ylabel('Count')
380
ax1.set_title('Distance Distribution')
381
382
# Plot 3D points and nearest surface points
383
ax2 = fig.add_subplot(122, projection='3d')
384
ax2.scatter(query_points[:, 0], query_points[:, 1], query_points[:, 2],
385
c='red', alpha=0.6, label='Query Points')
386
ax2.scatter(closest_points[:, 0], closest_points[:, 1], closest_points[:, 2],
387
c='blue', alpha=0.6, label='Nearest Surface Points')
388
ax2.legend()
389
ax2.set_title('Query Points and Nearest Surface Points')
390
391
plt.tight_layout()
392
plt.show()
393
```
394
395
### Point Containment Testing
396
397
```python
398
# Generate test points
399
test_points = np.random.uniform(*mesh.bounds.T, (1000, 3))
400
401
# Test containment
402
inside = mesh.contains(test_points)
403
outside_points = test_points[~inside]
404
inside_points = test_points[inside]
405
406
print(f"Points inside mesh: {inside.sum()}")
407
print(f"Points outside mesh: {(~inside).sum()}")
408
print(f"Containment ratio: {inside.mean():.3f}")
409
410
# Compute signed distances
411
signed_distances = mesh.signed_distance(test_points)
412
print(f"Signed distance range: {signed_distances.min():.4f} to {signed_distances.max():.4f}")
413
414
# Visualize containment
415
fig = plt.figure(figsize=(10, 8))
416
ax = fig.add_subplot(111, projection='3d')
417
418
if len(inside_points) > 0:
419
ax.scatter(inside_points[:, 0], inside_points[:, 1], inside_points[:, 2],
420
c='green', alpha=0.6, s=20, label=f'Inside ({len(inside_points)} points)')
421
422
if len(outside_points) > 0:
423
ax.scatter(outside_points[:, 0], outside_points[:, 1], outside_points[:, 2],
424
c='red', alpha=0.6, s=20, label=f'Outside ({len(outside_points)} points)')
425
426
ax.legend()
427
ax.set_title('Point Containment Test')
428
plt.show()
429
```
430
431
### Collision Detection
432
433
```python
434
# Create two meshes for collision testing
435
sphere1 = trimesh.primitives.Sphere(center=[0, 0, 0], radius=1.0)
436
sphere2 = trimesh.primitives.Sphere(center=[1.5, 0, 0], radius=1.0)
437
sphere3 = trimesh.primitives.Sphere(center=[0.5, 0, 0], radius=1.0)
438
439
# Test collisions
440
collision1 = sphere1.collision(sphere2) # Should not collide
441
collision2 = sphere1.collision(sphere3) # Should collide
442
443
print(f"Sphere1 vs Sphere2 collision: {collision1['collision']}")
444
print(f"Sphere1 vs Sphere3 collision: {collision2['collision']}")
445
446
if collision2['collision']:
447
print(f"Contact points:\n{collision2['contact_points']}")
448
print(f"Contact normals:\n{collision2['contact_normals']}")
449
print(f"Penetration depth: {collision2['penetration_depth']:.4f}")
450
451
# Fast boolean collision test
452
is_colliding = sphere1.is_collision(sphere3)
453
print(f"Fast collision test: {is_colliding}")
454
455
# Compute overlap volume
456
overlap_vol = sphere1.overlap_volume(sphere3)
457
print(f"Overlap volume: {overlap_vol:.6f}")
458
```
459
460
### Distance Analysis
461
462
```python
463
# Load two meshes for comparison
464
mesh1 = trimesh.load('part1.stl')
465
mesh2 = trimesh.load('part2.stl')
466
467
# Comprehensive distance analysis
468
distance_info = mesh1.distance_to_mesh(mesh2)
469
470
print("Distance Analysis:")
471
print(f"Mean distance: {distance_info['mean_distance']:.6f}")
472
print(f"RMS distance: {distance_info['rms_distance']:.6f}")
473
print(f"Hausdorff distance: {distance_info['hausdorff_distance']:.6f}")
474
print(f"Minimum distance: {distance_info['min_distance']:.6f}")
475
476
# Sample points on mesh1 surface and find distances to mesh2
477
sample_points = mesh1.sample_surface(1000)
478
closest_points, distances, face_indices = mesh2.closest_point_cloud(sample_points)
479
480
# Analyze distance distribution
481
print(f"\nSurface sampling analysis:")
482
print(f"Mean distance: {distances.mean():.6f}")
483
print(f"Std deviation: {distances.std():.6f}")
484
print(f"95th percentile: {np.percentile(distances, 95):.6f}")
485
486
# Create distance color map
487
normalized_distances = (distances - distances.min()) / (distances.max() - distances.min())
488
colors = plt.cm.viridis(normalized_distances)
489
490
# Visualize distance-colored points
491
fig = plt.figure(figsize=(10, 8))
492
ax = fig.add_subplot(111, projection='3d')
493
scatter = ax.scatter(sample_points[:, 0], sample_points[:, 1], sample_points[:, 2],
494
c=distances, cmap='viridis', s=20)
495
plt.colorbar(scatter, label='Distance')
496
ax.set_title('Distance-Colored Surface Points')
497
plt.show()
498
```
499
500
### Spatial Acceleration
501
502
```python
503
# Build spatial acceleration structures
504
mesh.build_kdtree()
505
print("KD-tree built for vertex queries")
506
507
# Query points within radius
508
query_center = mesh.centroid
509
radius = 0.5
510
511
nearby_vertices = mesh.query_ball_point([query_center], radius)[0]
512
print(f"Found {len(nearby_vertices)} vertices within radius {radius}")
513
514
# Get coordinates of nearby vertices
515
nearby_coords = mesh.vertices[nearby_vertices]
516
print(f"Nearby vertex coordinates:\n{nearby_coords}")
517
518
# For large-scale proximity queries between meshes
519
other_mesh = trimesh.load('other_model.stl')
520
other_mesh.build_kdtree()
521
522
# Find vertex pairs within threshold distance
523
vertex_pairs = mesh.query_ball_tree(other_mesh.kdtree, radius=0.1)
524
print(f"Found {len(vertex_pairs)} vertex pairs within threshold")
525
```