curvature
triwarp.curvature
Discrete curvature: principal directions and magnitudes, Gaussian and mean.
Three quantities computed three different ways, because on a mesh "curvature" is not one thing:
principal_curvaturefits a quadric to a geodesic-ball neighbourhood of each vertex and reads the shape operator off it, giving both principal directions and magnitudes. This is the extrinsic, neighbourhood-scale answer, and the only one with aradiusto tune (igl::principal_curvature).discrete_gaussian_curvatureis the angle defect: purely intrinsic, exact rather than fitted, and computable from the angles alone.discrete_mean_curvaturesums dihedral angle times edge length over the one ring, which is the integrated mean curvature rather than a pointwise one.
The neighbourhood the first one fits over is a geodesic ball
(geodesic_ball), not a Euclidean one: a spatial query would
pull in vertices across a fold of the surface and corrupt the fit.
principal_curvature
principal_curvature(
vertices: array[vec3],
faces: array[int32],
radius: int = 5,
*,
frame_independent: bool = True,
face_normals: array[vec3] | None = None,
face_areas: array[float32] | None = None,
) -> tuple[
wp.array[wp.vec3],
wp.array[wp.vec3],
wp.array[wp.float32],
wp.array[wp.float32],
]
Principal curvature directions and magnitudes per vertex via quadric fitting.
For each vertex a quadric surface is fitted to a sphere-neighborhood of vertices in the
local tangent frame. The principal curvatures and directions are extracted from the
eigendecomposition of the resulting shape operator (Weingarten map), built over a
sphere-search neighborhood of radius radius * avg_edge_length.
(PD1, PD2) is an orthonormal pair at every vertex where the fit succeeded, and it is
so by construction rather than by luck: principal directions are orthogonal, so only the first
is solved for and the second is its quarter turn about the vertex normal. Both are unit and
both lie in the tangent plane the fit used.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
vertices
|
array[vec3]
|
|
required |
faces
|
array[int32]
|
Length- |
required |
radius
|
int
|
Neighborhood size multiplier applied to the average edge length. Larger values collect more neighbors and produce smoother curvature estimates. |
5
|
frame_independent
|
bool
|
When |
True
|
face_normals
|
array[vec3] | None
|
Optional length- |
None
|
face_areas
|
array[float32] | None
|
See |
None
|
Returns:
| Type | Description |
|---|---|
tuple[array[vec3], array[vec3], array[float32], array[float32]]
|
|
Raises:
| Type | Description |
|---|---|
RuntimeError
|
If |
Notes
A failed fit is signalled by that all-zero output and nothing else -- there is no separate
validity mask. The two states it conflates are a failed fit and a genuinely flat vertex, which
is why the test is worth stating: PD1 is zero only where the fit did not produce a frame.
The fit is ill conditioned at a near-flat vertex, and that is worth knowing before reading
a small curvature as a measurement: a perturbation of the vertex normals far below their own
precision moves the returned value there by a large relative amount, while barely moving it
anywhere the surface actually curves. Compare a small PV against the mesh's own curvature
scale rather than against zero.
Source code in triwarp/curvature.py
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 | |
discrete_gaussian_curvature
discrete_gaussian_curvature(
points: array[vec3],
vertices: array[vec3],
faces: array[int32],
face_angles: Array2dFloat32,
radius: float,
) -> wp.array[wp.float32]
Return the discrete Gaussian curvature measure of a sphere centered at each query point.
As detailed in Cohen-Steiner and Morvan, "Restricted Delaunay triangulations and normal cycle". This is the sum of vertex defects at all vertices within the radius for each point.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
points
|
array[vec3]
|
|
required |
vertices
|
array[vec3]
|
|
required |
faces
|
array[int32]
|
Length- |
required |
face_angles
|
Array2dFloat32
|
|
required |
radius
|
float
|
Sphere radius; may be zero when |
required |
Returns:
| Type | Description |
|---|---|
array[float32]
|
Length |
Raises:
| Type | Description |
|---|---|
RuntimeError
|
If |
See Also
vertex_defects
The pointwise angle defect this integrates. Same quantity, no scale: at radius = 0 with
the vertices as query points the two agree, and it is the ball measure rather than the
pointwise defect that converges under refinement.
discrete_mean_curvature
The mean-curvature measure over the same ball.
Source code in triwarp/curvature.py
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 204 205 206 207 208 209 210 211 212 213 214 215 216 | |
discrete_mean_curvature
discrete_mean_curvature(
points: array[vec3],
vertices: array[vec3],
faces: array[int32],
radius: float,
*,
face_adjacency: Array2dInt32 | None = None,
face_adjacency_edges: Array2dInt32 | None = None,
) -> wp.array[wp.float32]
Return the discrete mean curvature measure of a sphere centered at each query point.
As detailed in Cohen-Steiner and Morvan, "Restricted Delaunay triangulations and normal cycle". This is the sum of edge angles contained in the sphere for each point.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
points
|
array[vec3]
|
|
required |
vertices
|
array[vec3]
|
|
required |
faces
|
array[int32]
|
Length- |
required |
radius
|
float
|
Sphere radius which should typically be greater than zero. |
required |
face_adjacency
|
Array2dInt32 | None
|
Optional |
None
|
face_adjacency_edges
|
Array2dInt32 | None
|
Optional |
None
|
Returns:
| Type | Description |
|---|---|
array[float32]
|
Length |
Raises:
| Type | Description |
|---|---|
ValueError
|
If only one of |
RuntimeError
|
If |
Source code in triwarp/curvature.py
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 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 | |