|
1 | 1 | from __future__ import annotations |
2 | 2 |
|
| 3 | +import math |
| 4 | + |
3 | 5 | import pytest |
4 | 6 | import torch |
5 | 7 |
|
6 | | -from witwin.core import Box, Cylinder, Mesh, Sphere |
| 8 | +from witwin.core import Box, Cylinder, Mesh, Sphere, Torus |
7 | 9 |
|
8 | 10 |
|
9 | 11 | def _grid(): |
@@ -71,6 +73,26 @@ def test_geometry_construction_to_mesh_and_to_mask(geometry, segments, inside_po |
71 | 73 | assert torch.any(occupancy < 0.5) |
72 | 74 |
|
73 | 75 |
|
| 76 | +def test_torus_mesh_matches_analytic_extents_and_volume(): |
| 77 | + major_radius = 0.8 |
| 78 | + minor_radius = 0.2 |
| 79 | + torus = Torus(major_radius=major_radius, minor_radius=minor_radius, axis="z") |
| 80 | + vertices, faces = torus.to_mesh(segments=48) |
| 81 | + |
| 82 | + expected_extents = torch.tensor( |
| 83 | + [major_radius + minor_radius, major_radius + minor_radius, minor_radius], |
| 84 | + dtype=vertices.dtype, |
| 85 | + ) |
| 86 | + torch.testing.assert_close(vertices.abs().amax(dim=0), expected_extents, rtol=1e-6, atol=1e-6) |
| 87 | + |
| 88 | + triangles = vertices[faces].to(torch.float64) |
| 89 | + signed_volume = torch.sum( |
| 90 | + torch.sum(triangles[:, 0] * torch.cross(triangles[:, 1], triangles[:, 2], dim=1), dim=1) |
| 91 | + ) / 6.0 |
| 92 | + expected_volume = 2.0 * math.pi**2 * major_radius * minor_radius**2 |
| 93 | + assert abs(float(signed_volume)) == pytest.approx(expected_volume, rel=1e-2) |
| 94 | + |
| 95 | + |
74 | 96 | def test_mesh_roundtrip_preserves_world_vertices_and_faces(): |
75 | 97 | base = Box(position=(0.25, -0.15, 0.4), size=(0.5, 0.3, 0.7)) |
76 | 98 | vertices, faces = base.to_mesh() |
|
0 commit comments