Initializing 3D Canvas...

Principal Curvatures

2 min read1 page

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):

1. Stand on a surface and look in every possible direction (360 degrees). 2. In one specific direction, the surface will bend the most. This is 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.
python
1# Surface Curvature Introduction
2def analyze_surface_point(surface, u, v):
3 """At any point on a surface, how much does it bend?"""
4
5 # We are looking for two specific values:
6 # k1 = Maximum Curvature (The steepest bend)
7 # k2 = Minimum Curvature (The flattest bend)
8
9 # Once we have those, we can classify the surface:
10 # Gaussian Curvature (K) = k1 * k2
11 # Mean Curvature (H) = (k1 + k2) / 2
12
13 return k1, k2
Surface (Sphere / Cylinder / Saddle)
0.00
2 min read1 page

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. Imagine standing on a surface with a stick pointing straight Up (the Normal). 2. Take one step Forward. How much did the stick tilt? 3. Take one step Right. How much did the stick tilt? 4. The Weingarten Matrix encodes exactly this: it describes how the Normal vector tilts based on which direction you step. This completely defines the local shape!
python
1# The Weingarten Operator (Shape Operator)
2def get_weingarten_matrix(surface, u, v):
3 """Calculates how the Normal vector tilts as you move."""
4
5 # E, F, G = First Fundamental Form (Stretching)
6 # L, M, N = Second Fundamental Form (Bending)
7
8 # 2x2 Matrix mapping tangent movement to normal tilting
9 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 ]
13
14 return matrix
Move (Center / Forward / Right)
0.00
2 min read1 page

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:

1. Because the matrix is 2x2, it has exactly two Eigenvalues. 2. By solving a simple quadratic equation using the matrix's Trace and Determinant, we instantly get both Eigenvalues. 3. The largest Eigenvalue is k1 (Maximum Curvature). 4. The smallest Eigenvalue is k2 (Minimum Curvature). 5. We have found the principal curvatures without ever searching the 360 degrees!
python
1# Finding Principal Curvatures
2def find_k1_k2(weingarten_matrix):
3 """Solves the matrix for its max and min values."""
4
5 # We use Linear Algebra to find the 'Eigenvalues' of the matrix.
6 # The Weingarten Matrix is a 2x2 matrix, so it has 2 Eigenvalues.
7
8 # Calculate the Trace and Determinant
9 trace = matrix[0][0] + matrix[1][1]
10 det = (matrix[0][0] * matrix[1][1]) - (matrix[0][1] * matrix[1][0])
11
12 # Quadratic formula magic
13 root = math.sqrt(trace*trace - 4*det)
14
15 k1 = (trace + root) / 2
16 k2 = (trace - root) / 2
17
18 return k1, k2
Curvature (k1 / k2)
0.00
2 min read1 page

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. By plugging k1 back into the matrix equation, we calculate the exact 3D direction of Maximum curvature. 2. By plugging k2 back in, we get the exact 3D direction of Minimum curvature. 3. These two arrows (Eigenvectors) form a perfect 90-degree crosshair (they are orthogonal). 4. This crosshair sits perfectly flat on the surface (tangent to the normal).
python
1# Finding Principal Directions
2def find_directions(weingarten_matrix, k1, k2):
3 """Finds the actual 3D vectors pointing in the min/max directions."""
4
5 # We solve the matrix equation: [Matrix] * [Vector] = k1 * [Vector]
6 # The resulting Vector is the 'Eigenvector'.
7
8 # Direction of Maximum Curvature
9 dir1_u = weingarten_matrix[0][1]
10 dir1_v = k1 - weingarten_matrix[0][0]
11
12 # Direction of Minimum Curvature
13 dir2_u = weingarten_matrix[0][1]
14 dir2_v = k2 - weingarten_matrix[0][0]
15
16 # These are 2D vectors in the local UV plane.
17 # Convert them back to 3D XYZ space...
18 return direction1_3d, direction2_3d
Vectors (k1 / k2 / Crosshair)
0.00
2 min read1 page

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:

1. Gaussian Curvature (K): 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.
python
1# Classifying Shapes using K and H
2def classify_surface(k1, k2):
3 """Gaussian (K) and Mean (H) Curvatures."""
4
5 K = k1 * k2
6 H = (k1 + k2) / 2
7
8 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"
Color Map (Mean / Gaussian)
0.00
2 min read1 page

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. The red lines follow the paths of steepest curvature (k1). 2. The blue lines follow the paths of flattest curvature (k2). 3. Because Eigenvectors are mathematically proven to be perfectly orthogonal, this network forms a perfect 90-degree grid everywhere on the surface! 4. This grid is called the Principal Curvature Network. Architects use this network to divide complex freeform roofs into perfect rectangular glass panels.
python
1# Generating a Principal Curvature Grid
2def map_principal_network(surface):
3 """Draws flow lines along the steepest/flattest paths."""
4
5 network = []
6
7 for point in surface.get_points():
8 # 1. Calculate Eigenvectors at this point
9 k1_dir, k2_dir = get_principal_directions(surface, point)
10
11 # 2. Draw a small line segment pointing in the k1 direction
12 network.append(Line(point, point + k1_dir))
13
14 # 3. Draw a small line segment pointing in the k2 direction
15 network.append(Line(point, point + k2_dir))
16
17 return network
Grid Density
8.00
2 min read1 page

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. Local Fitting: For every point on the Bunny, the computer grabs the 5 or 6 closest neighboring points. 2. It mathematically fits a tiny, smooth 3D bowl or saddle to those points (a Quadric fit). 3. It runs the exact Weingarten Matrix math we just learned on that tiny smooth patch! 4. By doing this 10,000 times, we can perfectly analyze the aerodynamic flow, stress points, and shape classification of any organic 3D model.
python
1# Curvature on Arbitrary Meshes
2def analyze_mesh_curvature(mesh):
3 """The Stanford Bunny in Differential Geometry."""
4
5 # 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!
9
10 for vertex in mesh.vertices:
11 local_surface = fit_quadric_to_neighbors(vertex)
12 k1, k2 = solve_eigenvalues(local_surface)
13
14 # Color the vertex based on K (Gaussian) or H (Mean)
15 vertex.color = map_curvature_to_color(k1, k2)
Curvature Map (Mean / Gaussian)
0.00