Principal Curvatures
In the last article, we learned how to find the curvature (acceleration) of a 1D line. But what happens when we move to a 2D Surface (like a sphere or a saddle)? A surface bends in multiple directions at once!
Principal Curvatures (k1 and k2):
k1 (Maximum Curvature). 3. In another direction, the surface will bend the least. This is k2 (Minimum Curvature). 4. By a miracle of differential geometry, these two directions are always exactly 90 degrees apart.1# Surface Curvature Introduction2def analyze_surface_point(surface, u, v):3 """At any point on a surface, how much does it bend?"""45 # We are looking for two specific values:6 # k1 = Maximum Curvature (The steepest bend)7 # k2 = Minimum Curvature (The flattest bend)89 # Once we have those, we can classify the surface:10 # Gaussian Curvature (K) = k1 * k211 # Mean Curvature (H) = (k1 + k2) / 21213 return k1, k2
How does a computer actually find those maximum and minimum bend directions? It doesn't just guess 360 degrees. It uses a 2x2 matrix called the Weingarten Operator (or Shape Operator).
The Normal Tilt:
1# The Weingarten Operator (Shape Operator)2def get_weingarten_matrix(surface, u, v):3 """Calculates how the Normal vector tilts as you move."""45 # E, F, G = First Fundamental Form (Stretching)6 # L, M, N = Second Fundamental Form (Bending)78 # 2x2 Matrix mapping tangent movement to normal tilting9 matrix = [10 [ (L*G - M*F) / (E*G - F*F), (M*E - L*F) / (E*G - F*F) ],11 [ (M*G - N*F) / (E*G - F*F), (N*E - M*F) / (E*G - F*F) ]12 ]1314 return matrix
Once we have the 2x2 Weingarten Matrix that describes how the Normal tilts, we need to extract theMaximum and Minimum possible tilts from it. In Linear Algebra, these extremes are called Eigenvalues.
Linear Algebra Magic:
k1 (Maximum Curvature). 4. The smallest Eigenvalue is k2 (Minimum Curvature). 5. We have found the principal curvatures without ever searching the 360 degrees!1# Finding Principal Curvatures2def find_k1_k2(weingarten_matrix):3 """Solves the matrix for its max and min values."""45 # We use Linear Algebra to find the 'Eigenvalues' of the matrix.6 # The Weingarten Matrix is a 2x2 matrix, so it has 2 Eigenvalues.78 # Calculate the Trace and Determinant9 trace = matrix[0][0] + matrix[1][1]10 det = (matrix[0][0] * matrix[1][1]) - (matrix[0][1] * matrix[1][0])1112 # Quadratic formula magic13 root = math.sqrt(trace*trace - 4*det)1415 k1 = (trace + root) / 216 k2 = (trace - root) / 21718 return k1, k2
We know how much the surface bends (the Eigenvalues k1 and k2). Now we need to knowwhich direction to look to see that bend. In Linear Algebra, these directional arrows are called Eigenvectors.
Orthogonal Crosshairs:
1# Finding Principal Directions2def find_directions(weingarten_matrix, k1, k2):3 """Finds the actual 3D vectors pointing in the min/max directions."""45 # We solve the matrix equation: [Matrix] * [Vector] = k1 * [Vector]6 # The resulting Vector is the 'Eigenvector'.78 # Direction of Maximum Curvature9 dir1_u = weingarten_matrix[0][1]10 dir1_v = k1 - weingarten_matrix[0][0]1112 # Direction of Minimum Curvature13 dir2_u = weingarten_matrix[0][1]14 dir2_v = k2 - weingarten_matrix[0][0]1516 # These are 2D vectors in the local UV plane.17 # Convert them back to 3D XYZ space...18 return direction1_3d, direction2_3d
Once a computer has calculated k1 and k2 for every point on a 3D model, it can classify the type of shape it is looking at. We do this by combining the two values.
K and H Metrics:
k1 * k2. If K is positive, the surface is a dome (bending the same way in both directions). If K is negative, the surface is a saddle (bending up in one direction, down in the other). 2. Mean Curvature (H): (k1 + k2) / 2. This is the average bend. Soap films and tension structures always physically minimize their Mean Curvature to exactly zero! 3. Engineers use these metrics to analyze sheet metal stress, aerodynamics, and architecture.1# Classifying Shapes using K and H2def classify_surface(k1, k2):3 """Gaussian (K) and Mean (H) Curvatures."""45 K = k1 * k26 H = (k1 + k2) / 278 if K > 0:9 return "Dome / Bowl (Synclastic)"10 elif K < 0:11 return "Saddle (Anticlastic)"12 elif K == 0 and H != 0:13 return "Cylinder / Cone (Developable)"14 elif K == 0 and H == 0:15 return "Flat Plane"
If we calculate the Eigenvectors (k1 and k2 directions) for thousands of points across a surface, and we draw short lines for all of them, a magical pattern emerges.
The Flow Network:
1# Generating a Principal Curvature Grid2def map_principal_network(surface):3 """Draws flow lines along the steepest/flattest paths."""45 network = []67 for point in surface.get_points():8 # 1. Calculate Eigenvectors at this point9 k1_dir, k2_dir = get_principal_directions(surface, point)1011 # 2. Draw a small line segment pointing in the k1 direction12 network.append(Line(point, point + k1_dir))1314 # 3. Draw a small line segment pointing in the k2 direction15 network.append(Line(point, point + k2_dir))1617 return network
How do we calculate curvature on a messy polygon mesh like the Stanford Bunny, where there are no smooth math equations, only jagged flat triangles?
Discrete Differential Geometry:
1# Curvature on Arbitrary Meshes2def analyze_mesh_curvature(mesh):3 """The Stanford Bunny in Differential Geometry."""45 # 1. We don't have a perfect mathematical equation for a Bunny.6 # 2. Instead, for every vertex, we look at its 1-ring neighbors.7 # 3. We fit a temporary, mathematical paraboloid surface to those points.8 # 4. We calculate the Weingarten Operator for that temporary surface!910 for vertex in mesh.vertices:11 local_surface = fit_quadric_to_neighbors(vertex)12 k1, k2 = solve_eigenvalues(local_surface)1314 # Color the vertex based on K (Gaussian) or H (Mean)15 vertex.color = map_curvature_to_color(k1, k2)