ordito.measures
What the whole mesh adds up to: the volume it encloses, its moments and its Euler characteristic.
Every function here reduces the entire surface to a single host-side number (or a small tuple of
them), which is why they are gathered rather than left beside the per-element fields they sum over.
Each therefore costs at least one host readback and forces a device synchronisation -- call them
once and keep the answer, and reach for ordito.reduce when a reduction should
stay on the device.
Three of the four are mass properties of the enclosed solid, and all three are only meaningful
on a closed, consistently wound surface -- is_volume is the check
for that, and make_volume the repair.
volume is the signed volume,
moments adds the centre of mass and the inertia tensor, and
surface_centroid is the one member that is a normalized
total rather than a sum: the area-weighted mean of the per-face centroids. It is a property of the
shell, so it differs from moments' centre of mass on any solid whose mass is not distributed
like its surface -- and it needs no watertightness, since a surface has a centroid whether or not it
bounds anything.
euler_characteristic is the odd one out: a topological
invariant rather than a measurement, counted off the index buffer with no geometry involved. It sits
here because it is a whole-mesh integer, and it is what
homology_generators derives the genus from.
Referenced by 1 place
API Reference API reference › Find a function by task
Functions:
| Name | Description |
|---|---|
volume |
Signed volume enclosed by the mesh. |
surface_centroid |
Area-weighted centroid of the mesh surface: the mean of the per-face centroids by area. |
moments |
Mass properties of the solid bounded by the mesh: volume, centre of mass and inertia tensor. |
euler_characteristic |
Euler characteristic |
volume
volume(
vertices: array[vec3] | array[vec3d],
faces: array[int32],
) -> float
Signed volume enclosed by the mesh.
Sum of per-face signed tetrahedron volumes measured from the origin
(dot(v0, cross(v1, v2)) / 6); for a closed, consistently wound surface this is
independent of the reference point, and its sign follows the orientation of the face
normals (positive for outward-facing normals). For an open or inconsistently wound mesh
the result is not a meaningful volume.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
vertices
|
array[vec3] | array[vec3d]
|
|
required |
faces
|
array[int32]
|
|
required |
Returns:
| Type | Description |
|---|---|
float
|
Signed volume, accumulated in |
Raises:
| Type | Description |
|---|---|
RuntimeError
|
If |
See Also
face_signed_volumes
The per-face primitive this sums.
is_volume
Whether the sum means anything on this mesh.
make_volume
Make it mean something.
moments
The same volume in float64, plus the centre of mass and inertia.
filter_laplacian
The float64 consumer: its volume constraint rescales the mesh to hold this fixed.
trimesh.Trimesh.volume
Examples:
A unit cube encloses volume 1; an icosphere approaches the unit ball's 4 * pi / 3 from
below:
import ordito as od
vertices, faces = od.creation.box()
print(f"{od.measures.volume(vertices, faces):.4f}") # 1.0000
vertices, faces = od.creation.icosphere(subdivisions=3)
print(f"{od.measures.volume(vertices, faces):.3f}") # 4.153
Referenced by 13 places
API Reference API reference › Find a function by task · measures · measures › moments · repair › make_volume · transform › transform_mesh · Trimesh › volume · triangles › face_signed_volumes · validation › is_volume
Concepts Reproducible results › What it costs · Reproducible results › What it looks like
Examples Reproducible results › Sums that repeat bit for bit · Smoothing and fairing › Smoothing without shrinking
Migrating from another library Migrating from Open3D › Surface reconstruction and meshes
Source code in ordito/measures.py
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 | |
surface_centroid
surface_centroid(
vertices: array[vec3], faces: array[int32]
) -> wp.vec3
Area-weighted centroid of the mesh surface: the mean of the per-face centroids by area.
A normalized total rather than a sum, and a property of the shell alone -- unlike
moments' centre of mass it needs no watertightness and no
consistent winding, because a surface has a centroid whether or not it bounds a solid. The two
differ on any solid whose mass is not distributed like its surface.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
vertices
|
array[vec3]
|
|
required |
faces
|
array[int32]
|
|
required |
Returns:
| Type | Description |
|---|---|
vec3
|
Area-weighted mean of per-face centroids. All- |
Raises:
| Type | Description |
|---|---|
RuntimeError
|
If |
See Also
moments
The centre of mass of the enclosed solid, which this is not.
face_centroids
The per-face barycentres this averages.
centroid
The unweighted mean of a point cloud, and the only other centroid in the package.
trimesh.Trimesh.centroid
Examples:
import ordito as od
vertices, faces = od.creation.box(bounds=[[0.0, 0.0, 0.0], [2.0, 4.0, 6.0]])
print(od.measures.surface_centroid(vertices, faces)) # [1.0, 2.0, 3.0]
Referenced by 4 places
API Reference measures · measures › moments · Trimesh › centroid · triangles › face_centroids
Source code in ordito/measures.py
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 | |
moments
moments(
vertices: array[vec3], faces: array[int32]
) -> tuple[float, wp.vec3, wp.mat33d]
Mass properties of the solid bounded by the mesh: volume, centre of mass and inertia tensor.
Integrates over the enclosed solid at unit density by summing the contribution of every
(origin, face) tetrahedron, so the result is only meaningful for a closed, consistently
wound surface -- the same precondition volume carries, and
is_volume is the check for it.
The integrals accumulate in float64 even though the positions are float32: the second
moments scale as length ** 5, so a float32 sum loses their low digits on any sizeable
mesh.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
vertices
|
array[vec3]
|
|
required |
faces
|
array[int32]
|
|
required |
Returns:
| Name | Type | Description |
|---|---|---|
volume |
float
|
Signed volume, identical to |
center_of_mass |
vec3
|
Volume centroid, i.e. the first moment divided by the volume. This is not
|
inertia |
mat33d
|
|
Raises:
| Type | Description |
|---|---|
RuntimeError
|
If |
Notes
igl.moments returns (m0, m1, m2) where m1 is the first moment -- the centre of mass
times the mass -- rather than the centre of mass itself, and m2 is already referred to the
centre of mass (verified against a translated mesh, not assumed). This function returns the
decoded forms, so the transform between the two is m1 / m0.
One host readback is unavoidable here: all three returns are host-side values, so the ten accumulated sums have to cross the device boundary to be combined. They cross together.
See Also
volume
surface_centroid
is_volume
trimesh.Trimesh.moment_inertia
igl.moments
Examples:
A 2 x 4 x 6 box of unit density has mass 48 and principal moments
48 * (b**2 + c**2) / 12 about its centre:
import ordito as od
vertices, faces = od.creation.box(bounds=[[0.0, 0.0, 0.0], [2.0, 4.0, 6.0]])
volume, center, inertia = od.measures.moments(vertices, faces)
print(f"{volume:.3f}") # 48.000
print(center) # [1.0, 2.0, 3.0]
print([round(float(inertia[i, i]), 3) for i in range(3)]) # [208.0, 160.0, 80.0]
Referenced by 8 places
API Reference API reference › Find a function by task · measures · measures › surface_centroid · measures › volume · Trimesh › center_mass · Trimesh › moment_inertia · Trimesh › volume · triangles › face_centroids
Source code in ordito/measures.py
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 | |
euler_characteristic
euler_characteristic(
faces: array[int32], *, n_vertices: int | None = None
) -> int
Euler characteristic V - E + F of the mesh (topological invariant).
Counts distinct referenced vertices, unique undirected edges, and faces, matching
trimesh.Trimesh.euler_number (which uses referenced vertices, edges_unique, and
faces). For a closed genus-g surface this equals 2 - 2 * g.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
faces
|
array[int32]
|
|
required |
n_vertices
|
int | None
|
A bound on the indices in |
None
|
Returns:
| Type | Description |
|---|---|
int
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Examples:
A sphere has chi = 2 and a torus chi = 0:
import ordito as od
vertices, faces = od.creation.icosphere(subdivisions=2)
print(od.measures.euler_characteristic(faces)) # 2
vertices, faces = od.creation.torus(1.0, 0.3, major_sections=16, minor_sections=8)
print(od.measures.euler_characteristic(faces)) # 0
Referenced by 7 places
API Reference API reference › Find a function by task · measures · repair › remove_tunnels · homology · homology › homology_generators · Trimesh › euler_characteristic
Examples Repairing meshes › Removing tunnels
Source code in ordito/measures.py
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 | |