Signed Distance Fields in Python
Contents
A signed distance field (SDF) assigns to every point $\mathbf{x}$ in space its signed distance to the nearest surface: negative inside, zero on the surface, positive outside. SDFs enable smooth boolean operations, offsetting, and collision proximity queries without explicit mesh representation.
Definition and properties
For a shape $\Omega$ with boundary $\partial\Omega$:
\[d(\mathbf{x}) = \begin{cases} -\min_{\mathbf{p}\in\partial\Omega}\|\mathbf{x}-\mathbf{p}\| & \text{if } \mathbf{x} \in \Omega \\ +\min_{\mathbf{p}\in\partial\Omega}\|\mathbf{x}-\mathbf{p}\| & \text{if } \mathbf{x} \notin \Omega \end{cases} \tag{1}\]| An SDF satisfies the Eikonal equation almost everywhere: $ | \nabla d | = 1$. |
Primitive SDFs
Circle (radius $r$, center $\mathbf{c}$):
\[d_{\rm circle}(\mathbf{x}) = \|\mathbf{x} - \mathbf{c}\| - r \tag{2}\]Axis-aligned rectangle (half-extents $\mathbf{b} = (b_x, b_y)$, centered at origin):
\[d_{\rm rect}(\mathbf{x}) = \|\max(\mathbf{q}, 0)\| + \min(\max(q_x, q_y),\, 0), \qquad \mathbf{q} = |\mathbf{x}| - \mathbf{b} \tag{3}\]Line segment (endpoints $\mathbf{a}$, $\mathbf{b}$):
\[d_{\rm seg}(\mathbf{x}) = \left\|\mathbf{x} - \mathbf{a} - \mathrm{clamp}\!\left(\frac{(\mathbf{x}-\mathbf{a})\cdot(\mathbf{b}-\mathbf{a})}{\|\mathbf{b}-\mathbf{a}\|^2},\, 0,\, 1\right)(\mathbf{b}-\mathbf{a})\right\| \tag{4}\]Boolean operations and smooth blend
Union, intersection, subtraction via min/max:
\[d_{\rm union} = \min(d_1, d_2), \qquad d_{\rm intersect} = \max(d_1, d_2), \qquad d_{\rm subtract} = \max(d_1, -d_2) \tag{5}\]Smooth union (Quilez polynomial smooth minimum; $k$ controls blend radius):
\[{\rm smin}(a, b, k) = \min(a, b) - \frac{1}{4k}\left[\max(k - |a-b|,\, 0)\right]^2 \tag{6}\]Interactive: 2D SDF contour plot
Select a scene and adjust the blend radius. The heatmap shows $d(\mathbf{x})$ over $[-3,3]^2$; the thick black contour is the shape boundary $d=0$.
Python
import numpy as np
import matplotlib.pyplot as plt
def sdf_circle(x, y, cx=0, cy=0, r=1.0):
return np.sqrt((x - cx)**2 + (y - cy)**2) - r
def sdf_rect(x, y, bx=1.0, by=0.5):
qx = np.abs(x) - bx
qy = np.abs(y) - by
return (np.sqrt(np.maximum(qx, 0)**2 + np.maximum(qy, 0)**2)
+ np.minimum(np.maximum(qx, qy), 0))
def smin(a, b, k=0.3):
h = np.maximum(k - np.abs(a - b), 0) / k
return np.minimum(a, b) - h**2 * k / 4
# Evaluate on grid
N = 200
x = np.linspace(-3, 3, N)
y = np.linspace(-3, 3, N)
X, Y = np.meshgrid(x, y)
d = smin(sdf_circle(X, Y, -0.5, 0, 0.9), sdf_rect(X + 0.3, Y, 0.8, 0.5))
plt.figure(figsize=(5, 5))
plt.contourf(X, Y, d, levels=50, cmap='RdBu_r', vmin=-2, vmax=2)
plt.contour(X, Y, d, levels=[0], colors='k', linewidths=2)
plt.axis('equal')
plt.colorbar(label='d(x)')
plt.title('Smooth union SDF')
plt.show()
References
Hart, J. C. (1996). Sphere tracing: A geometric method for the antialiased ray tracing of implicit surfaces. The Visual Computer, 12(10), 527β545.
Quilez, I. (various). Distance functions. Retrieved from https://iquilezles.org/articles/distfunctions2d/
Osher, S. & Sethian, J. A. (1988). Fronts propagating with curvature-dependent speed. Journal of Computational Physics, 79(1), 12β49.
SDFs compose naturally: any boolean or blend operation remains a single distance evaluation per point.
Basem Rajjoub