Library · Geometric Probability · Chapter 58
Cauchy’s shadow formula
Problem 58.1
Let be a convex body in with surface area . For a direction chosen uniformly on the unit sphere, let be the area of the shadow of onto a plane perpendicular to . What is ?
Solution. Write the boundary of as . At each point , let be the outward unit normal. For a direction , a boundary element at is sun-facing (visible to a light source at infinity in the direction ) iff . Each sun-facing element contributes to the area of the shadow, since its orthogonal projection onto the shadow plane has that size. Hence
Average over uniform on the sphere and exchange the order of integration (Fubini):
For a fixed unit vector , the quantity is the cosine of the angle between and . For uniform on the sphere, this angle has density on (the standard spherical Jacobian). Hence
Therefore In particular, the unit cube (surface area ) casts an expected shadow of area . ■
Cauchy’s formula is the three-dimensional analogue of the fact that the average width of a convex plane curve equals its perimeter divided by . In general dimension , Cauchy’s surface-area formula concerns projections onto -dimensional hyperplanes: their average volume is a universal multiple of the body’s surface area. Lower-dimensional projection averages involve the corresponding intrinsic volumes, rather than surface area.
import numpy as np
from scipy.spatial import ConvexHull
# For each uniform random direction d, project the 8 unit-cube vertices onto
# a plane perpendicular to d and compute the area of their convex hull.
N = 10**4
d = np.random.randn(3, N); d /= np.linalg.norm(d, axis=0)
verts = np.array([[x, y, z] for x in [-.5, .5]
for y in [-.5, .5]
for z in [-.5, .5]]).T # 3 x 8
areas = np.empty(N)
for i in range(N):
di = d[:, i]
h = np.array([1.0, 0, 0]) if abs(di[0]) < 0.9 else np.array([0.0, 1, 0])
e1 = np.cross(di, h); e1 /= np.linalg.norm(e1)
e2 = np.cross(di, e1)
proj = np.column_stack([verts.T @ e1, verts.T @ e2])
areas[i] = ConvexHull(proj).volume # 2D "volume" = area
print(f"sim: {areas.mean():.5f} exact: 1.5")
# sim: 1.50060 exact: 1.5