Gaussian Curvature
In Differential Geometry, we classify the shape of a surface based on how it bends.Gaussian Curvature (K) is an intrinsic property, meaning it doesn't change even if you bend the surface (without stretching it).
Curvature Types:
1# Gaussian Curvature Theory2def understand_curvature(K):3 """K = k1 * k2 (the two principal curvatures)"""45 if K > 0:6 return "Positive (Synclastic): Like a Sphere. Bends same way in all directions."7 elif K < 0:8 return "Negative (Anticlastic): Like a Saddle. Bends up in one direction, down in another."9 else:10 return "Zero (Developable): Like a Cylinder or flat plane. Unrolls flat without tearing."
In a continuous math formula, curvature is measured at an infinitely small point. But in a discrete Triangle Mesh, curvature can only be approximated by looking at a vertex and its immediate neighbors.
Discrete Differential Geometry:
1# Finding the 1-Ring Neighborhood2def get_vertex_star(mesh, vertex_id):3 """Returns all faces and vertices immediately connected to a vertex."""45 # We query the topology graph6 connected_faces = mesh.topology.get_faces_at_vertex(vertex_id)7 connected_vertices = mesh.topology.get_neighbors(vertex_id)89 return connected_faces, connected_vertices
Because curvature is a measure of how bending changes over an area, we need to know exactly how much surface area "belongs" to our central vertex.
Area Weighting:
1# Voronoi Area of a Vertex2def calculate_mixed_voronoi_area(vertex_id, star_faces):3 """Calculates the surface area 'belonging' to a single vertex."""45 total_area = 0.067 for face in star_faces:8 # A simple approximation (Barycentric area) is 1/3 of each triangle's area9 tri_area = calculate_triangle_area(face)1011 # In Meyer's true algorithm, we use the Mixed Voronoi Area12 # which accounts for obtuse (wide) triangles, but 1/3 is often good enough!13 total_area += (tri_area / 3.0)1415 return total_area
How do we know if a vertex is flat, peaked, or saddle-shaped just by looking at its triangles? We use the Angle Deficit.
The 360 Rule:
1# Calculating Angle Deficit2def calculate_angle_deficit(vertex_id, star_faces):3 """How much a vertex behaves like a cone vs a saddle."""45 # Start with 2*PI (a completely flat, 360 degree circle)6 angle_sum = 2.0 * math.pi78 for face in star_faces:9 # Find the angle of this specific triangle at the central vertex10 theta = calculate_triangle_angle(face, center=vertex_id)1112 # Subtract it from our total13 angle_sum -= theta1415 # The remainder is the 'Angle Deficit'16 return angle_sum
We have the Angle Deficit (the raw bending amount) and the Voronoi Area (the size of the neighborhood). To get the final Gaussian Curvature (K), we simply divide the Deficit by the Area.
Mesh Independence:
1# Calculating Discrete Gaussian Curvature2def get_gaussian_curvature(vertex_id, mesh):3 """Combines Angle Deficit with Voronoi Area."""45 star_faces = mesh.topology.get_faces(vertex_id)67 # 1. Get the raw angular deficit8 angle_deficit = calculate_angle_deficit(vertex_id, star_faces)910 # 2. Get the surface area belonging to this vertex11 voronoi_area = calculate_mixed_voronoi_area(vertex_id, star_faces)1213 # 3. Normalize!14 # Without this, a densely tessellated sphere would seem to have15 # less curvature per-vertex than a low-poly sphere.16 K = angle_deficit / voronoi_area1718 return K
Once we calculate Gaussian Curvature K for every single vertex on a mesh, we have an array of numbers. To make this data useful to humans, we map those numbers to Vertex Colors.
Color Mapping:
K value to a 0.0 - 1.0 percentage. 3. Map that percentage to a color gradient. Usually:- Red: High Positive (Peaks)
- Green/Grey: Zero (Flat)
- Blue: High Negative (Saddles/Valleys)
1# Vertex Color Mapping2def apply_curvature_colors(mesh, k_array):3 """Maps numerical curvature to an RGB gradient."""45 # K can be anything from -100 to +100 depending on scale.6 # We need to map this to a 0.0 to 1.0 color gradient.78 k_min = min(k_array)9 k_max = max(k_array)1011 for vertex_id, K in enumerate(k_array):12 # Normalize K to a 0-1 range based on the mesh extremes13 normalized_k = (K - k_min) / (k_max - k_min)1415 # Look up color in a gradient map (e.g. Blue -> Green -> Red)16 color = sample_gradient(normalized_k)1718 mesh.vertices[vertex_id].color = color
In advanced 3D modeling tools (like Rhino or Alias), Curvature Analysis is used constantly. Car designers use it to ensure the sheet metal is perfectly smooth without any hidden wobbles. 3D Printers use it to identify sharp peaks that might melt or break off.
Visual Diagnostics:
1# Complete Curvature Pipeline2def analyze_surface_curvature(mesh):3 """Full discrete differential geometry analysis."""45 curvature_data = []67 for vertex in mesh.vertices:8 # 1. Topological Analysis (1-Ring)9 star = get_vertex_star(mesh, vertex.id)1011 # 2. Geometric Analysis12 deficit = calculate_angle_deficit(vertex.id, star)13 area = calculate_mixed_voronoi_area(vertex.id, star)1415 # 3. Normalization16 K = deficit / area17 curvature_data.append(K)1819 # 4. Rendering20 apply_curvature_colors(mesh, curvature_data)2122 return mesh