Interactive Visual Guide

Triangle Deformation: Local Frames, Jacobians & UV Parameterization

Build intuition for how 3D surface triangles are projected into canonical 2D frames, how the per-face Jacobian measures the deformation between them, and how singular values classify distortion — the machinery behind ARAP, ACAP, and UV unwrapping.

Part 1 — Projecting a Single 3D Triangle into 2D

The Problem

A triangle in 3D space is defined by three vertices — v0, v1, and v2 — at arbitrary positions. The same triangle could appear at many different positions and rotations in the world, yet its shape (relative side lengths and angles) never changes.

To compare or process triangle geometry, we need a canonical 2D representation: a coordinate frame built from the triangle itself, where v0 sits at the origin and the base edge lies along the X-axis. The 2D coordinate of v2 in this frame fully describes the shape — no matter where the triangle was in 3D space.

INPUT — 3D World Space
Drag to rotate  |  Base $\vec{u}_1$    Offset $\vec{u}_2$
OUTPUT — 2D Local Frame
Base on X-axis  |  Shadow - -  |  Height
Move v2 in 3D space and watch the 2D frame update live:
Try: Slide only Z. The 3D triangle moves in depth, but the 2D output barely changes — Z has no effect on the base, so it contributes only a small height shift. This proves we're capturing shape, not world position.
The Math — Step by Step
1
Build the Local Coordinate Frame

Pin v0 at the origin. The base edge v0→v1 defines the local X-axis. Dividing by its length gives a unit vector $\hat{u}_1$ — our X-axis direction.

$$\vec{u}_1 = v_1 - v_0$$ $$L = \lVert\vec{u}_1\rVert, \qquad \hat{u}_1 = \frac{\vec{u}_1}{L}$$
# v0 and v1 are fixed
v0 = [1.0, 2.0, 1.0]
v1 = [5.0, 2.0, 1.0]
u1 = v1 − v0 = [4.00, 0.00, 0.00]
L = |u1| = 4.000
û1 = u1/L = [1.000, 0.000, 0.000]
2
Project v2 onto the Base — Shadow (X)

Express v2 relative to v0 to get the offset $\vec{u}_2$. The dot product of $\vec{u}_2$ with the unit base $\hat{u}_1$ gives the component that lies along the base — the local X-coordinate.

$$\vec{u}_2 = v_2 - v_0$$ $$x = \vec{u}_2 \cdot \hat{u}_1$$
# v2 moves with the sliders
v2 = [1.0, 5.0, 1.0]
u2 = v2 − v0 = [0.00, 3.00, 0.00]
# dot product = shadow on base axis
x = u2 · û1 = 0.000
3
Perpendicular Remainder — Height (Y)

Subtract the shadow component from $\vec{u}_2$, leaving only the part perpendicular to the base. Its length is the local Y-coordinate — the triangle's height.

$$\vec{u}_{2\perp} = \vec{u}_2 - x\,\hat{u}_1$$ $$y = \lVert\vec{u}_{2\perp}\rVert$$
# remove the base-aligned part
shadow = x · û1 = [0.00, 0.00, 0.00]
u2⊥ = u2 − shadow = [0.00, 3.00, 0.00]
# height = length of remainder
y = |u2⊥| = 3.000

Why Does the Dot Product Give Us the Shadow?

The dot product of any vector $\vec{u}_2$ with a unit vector $\hat{u}_1$ returns the scalar length of $\vec{u}_2$'s shadow cast onto the line defined by $\hat{u}_1$. Formally:

$$x = \vec{u}_2 \cdot \hat{u}_1 = \lVert\vec{u}_2\rVert \cos\theta$$

where $\theta$ is the angle between them. When $\theta = 0°$ the vectors are parallel and $x = \lVert\vec{u}_2\rVert$ (full length). When $\theta = 90°$ the vectors are perpendicular and $x = 0$ (no shadow).

Subtracting this shadow from $\vec{u}_2$ leaves a vector exactly perpendicular to $\hat{u}_1$ — the height component. Together, shadow and height give us a complete, orthogonal decomposition of $\vec{u}_2$ in the triangle's own frame.

Result: Canonical 2D Coordinates

v0′ — always
( 0, 0 )
v1′ — base endpoint
( 4.000, 0 )
v2′ — (shadow, height)
( 0.000, 3.000 )

These three 2D coordinates fully encode the triangle's geometry. Two triangles with the same $v_0',\, v_1',\, v_2'$ are congruent — no matter where they were in 3D space.

Part 2 — Deformation Gradient Between Two Triangles

What Is J? — The Map That Takes Source Edges to Target Edges

After Part 1, both triangles live in their own 2D frames. Pack each triangle's two edge vectors as columns of a matrix $M$. Then $J$ is simply the map that makes source edges become target edges:

$$J \cdot \underbrace{\begin{bmatrix}L_s & x_s \\ 0 & y_s\end{bmatrix}}_{M_s} = \underbrace{\begin{bmatrix}L_t & x_t \\ 0 & y_t\end{bmatrix}}_{M_t} \quad\Longrightarrow\quad J = M_t\,M_s^{-1} = \begin{bmatrix} \tfrac{L_t}{L_s} & \dfrac{x_t L_s - L_t x_s}{L_s\,y_s} \\[6pt] 0 & \tfrac{y_t}{y_s} \end{bmatrix}$$

Because $M$ is upper-triangular, each entry of $J$ has a direct geometric meaning:

J[0,0] = L_t / L_s
Base-edge scale — bottom edge stretch factor.
J[1,1] = y_t / y_s
Height scale — apex height stretch factor.
J[0,1] = shear
Apex lean — non-zero when the shapes differ in tilt.
J[1,0] = 0
Always zero — both frames pin the base on X.
Adjust source and target triangle shapes:
Triangle in 2D frame:   v0 = (0, 0)  ·  v1 = (L, 0)  ·  v2 = (x, y)
L = base length (v0 → v1 along X)   x = apex horizontal offset from v0   y = apex height above the base
Example: Ls=4, xs=1, ys=3 → source v2=(1, 3): base 4 wide, apex 1 right & 3 up from origin. Set Lt=8, xt=2, yt=6 and J ≈ 2·I — uniform 2× scale.
Source triangle (2D frame)
Target triangle (2D frame)
Quick presets
Identity — J = I, σ_t = 1 (no distortion)
Scale ×2 — uniform stretch, σ_t = 2
Stretch X — base doubled, height fixed
Shear — off-diagonal J entry > 0
Near-flat — y_t → 0, σ_t → ∞
ARAP-like — nearly rigid, σ_t ≈ 1
Source — 2D Frame
v0=(0,0)  v1=(Ls,0)  v2=(xs,ys)
Target — 2D Frame
v0=(0,0)  v1=(Lt,0)  v2=(xt,yt)
Deformation Ellipse
Unit circle mapped through J  |  semi-major axis = $\sigma_{\max}$, semi-minor axis = $\sigma_{\min}$

Live Jacobian   $J = M_t \, M_s^{-1}$

2×2 Deformation Gradient
J =
1.0000.000 0.0001.000
Singular Values
σmax =
σmin =
Interpretation
Distortion Metrics
det(J)
σ_t  isometric
σ_t isometric distortion  (1 = perfect, higher = worse)
1248+
# columns = 2D edge vectors
M_s = [[4.00, 1.00], [0, 3.00]]
M_t = [[4.00, 1.00], [0, 3.00]]
# J = M_t @ inv(M_s)
J[0,0] = Lt/Ls              = 1.000 ← base scale
J[1,1] = yt/ys              = 1.000 ← height scale
J[0,1] = (xt·Ls−Lt·xs)/(Ls·ys) = 0.000 ← shear
J[1,0] = 0                   ← always zero
det J = J00·J11            = 1.000
σ_t = max(σ_max,1/σ_min)  = 1.000
Part 3 — Rigid & Conformal Transformations

Classifying Jacobians: Rigid, Conformal, Non-Conformal

Any 2×2 Jacobian $J$ can be factored as $J = R(\theta)\,\mathrm{diag}(s_x,\,s_y)$ — first scale each axis independently, then rotate the whole result. Three components, three geometric meanings:

$R(\theta)$ — pure rotation
Spins the entire mesh by $\theta$ around the origin. No stretching — $R$ alone preserves all lengths and angles.
$R(\theta)=\begin{bmatrix}\cos\theta & -\sin\theta\\\sin\theta & \phantom{-}\cos\theta\end{bmatrix}$
θ=45° → every point orbits 45° around origin; all triangles unchanged in shape.
$s_x$ — base-direction scale
Scales the horizontal direction — along $\overrightarrow{v_0 v_1}$, the base edge from Part 1.
sx=2: base of every triangle doubles → mesh stretches left–right
sx=0.5: base halves → mesh compressed horizontally
sx=1: no horizontal change
$s_y$ — height-direction scale
Scales the vertical direction — perpendicular to the base, controlling triangle height.
sy=2: height of every triangle doubles → mesh stretches up–down
sy=0.5: height halves → mesh squashed toward base
sy=1: no vertical change
Worked Example — θ = 30°, sx = 2, sy = 0.5
rotation $R(30°)$
$\begin{bmatrix}0.866 & {-0.500}\\0.500 & \phantom{-}0.866\end{bmatrix}$
·
scaling $\mathrm{diag}(s_x,s_y)$
$\begin{bmatrix}2 & 0\\0 & 0.5\end{bmatrix}$
=
Jacobian $J$
$\begin{bmatrix}1.732 & {-0.250}\\1.000 & \phantom{-}0.433\end{bmatrix}$
Key insight:
$\sigma_{\max} = s_x = 2$
$\sigma_{\min} = s_y = 0.5$
Singular values = sx, sy regardless of θ. Rotation doesn't stretch — only diag(sx, sy) does.
$J = R(\theta)\,\mathrm{diag}(s_x,s_y) = \begin{bmatrix}\cos\theta\cdot s_x & -\sin\theta\cdot s_y\\\sin\theta\cdot s_x & \cos\theta\cdot s_y\end{bmatrix}$  →  row 0: $[0.866{\times}2,\;{-0.5}{\times}0.5] = [1.732,\;{-0.250}]$   row 1: $[0.5{\times}2,\;0.866{\times}0.5] = [1.000,\;0.433]$

The 4-triangle mesh below makes $J$ concrete. Select a triangle, drag the sliders, and watch only that triangle deform while the others stay fixed — just like per-face Jacobians in a real UV solver.

Real Example: UV Parameterization

UV parameterization cuts a 3D surface and unfolds it flat. Each 3D triangle maps to a 2D triangle in the UV plane — and the Jacobian of that map is exactly what Parts 1 & 2 computed.

3D triangle
Project v₀,v₁,v₂ to local frame (Part 1):
Ms = [[Ls, xs], [0, ys]]
→ J →
UV triangle
Pack UV coords into (Part 2):
Mt = [[Lt, xt], [0, yt]]
Distortion = J = MtMs−1
ARAP → 0: near-isometric (no stretch)
ACAP → 0: conformal (no shear)
$J = R(\theta)\,\mathrm{diag}(s_x,\,s_y)$  for triangle:
sliders update selected triangle only
Transformation Parameters
Quick presets
Transform type:
Source — 4-Triangle Mesh
Click a triangle button above to select it
After J — Per-Triangle Deformation
Each triangle has its own J  |  matching colors = correspondence

Live Jacobian — T1   $J = R(\theta)\,\mathrm{diag}(s_x,\,s_y)$

Jacobian Matrix
J =
1.0000.000 0.0001.000
Singular Values
σmax =
σmin =
Energy Metrics
det(J)
ARAP energy
ACAP energy
ARAP = $(\sigma_{\max}{-}1)^2 + (\sigma_{\min}{-}1)^2$ → 0 iff rigid
ACAP = $(\sigma_{\max}{-}\sigma_{\min})^2/2$ → 0 iff conformal
RIGID
Isometric (distance-preserving)

Both singular values equal 1. The Jacobian is a pure rotation: $J = R(\theta)$. Distances and angles are preserved.

$$J = R(\theta), \quad \sigma_{\max} = \sigma_{\min} = 1$$

Mesh triangles keep their shape — no stretching or shearing. Both ARAP and ACAP energies are zero.

CONF
Conformal (angle-preserving)

Both singular values are equal but $\ne 1$. The Jacobian is a scaled rotation: $J = s\,R(\theta)$. Angles preserved; area scales uniformly.

$$J = s\,R(\theta), \quad \sigma_{\max} = \sigma_{\min} = s \ne 1$$

Mesh scales uniformly — triangles grow or shrink but stay similar. ACAP energy = 0; ARAP energy ≠ 0.

NON-C
Non-conformal (angle-distorting)

Singular values differ: $\sigma_{\max} \ne \sigma_{\min}$. Space is stretched more along one axis — angles are not preserved.

$$J = R(\theta)\,\mathrm{diag}(s_x,s_y),\quad \sigma_{\max} \ne \sigma_{\min}$$

Mesh stretches differently along each axis — triangles become squashed or elongated. Both ARAP and ACAP energies are non-zero.

Conclusion — Putting It All Together

The Full Pipeline

Every idea in this tutorial feeds the next. Starting from raw 3D coordinates, three steps turn a pair of triangles into a single number measuring distortion:

P1
Local 2D Frame

Align the base edge with the X-axis. Every 3D triangle collapses to three numbers $(L,\,x,\,y)$ — base length, apex offset, apex height. The 3D embedding becomes irrelevant.

$$M = \begin{bmatrix}L & x \\ 0 & y\end{bmatrix}$$
P2
Per-Face Jacobian

$J = M_t M_s^{-1}$ is the unique linear map sending source edges to target edges. Its entries directly encode base scale, height scale, and shear. Apply it to any point in the source triangle to find where it lands in the target.

$$J = \begin{bmatrix}L_t/L_s & \cdot \\ 0 & y_t/y_s\end{bmatrix}$$
P3
SVD & Classification

Factor $J = R(\theta)\,\mathrm{diag}(\sigma_{\max},\sigma_{\min})$. The singular values are the semi-axes of the ellipse a unit circle maps to — they measure pure stretch independent of rotation.

$$\text{ARAP} = (\sigma_{\max}{-}1)^2{+}(\sigma_{\min}{-}1)^2, \quad \text{ACAP} = \tfrac{(\sigma_{\max}{-}\sigma_{\min})^2}{2}$$

Key Takeaways

Local frame = coordinate-free
By projecting each triangle into its own 2D frame, the comparison between triangles is independent of their position or orientation in 3D space.
J encodes all deformation
J[0,0] = base scale, J[1,1] = height scale, J[0,1] = shear, J[1,0] = 0 always. Every distortion question reduces to reading entries of a 2×2 matrix.
σ_max, σ_min are the stretch axes
They are the semi-axes of the ellipse a unit circle maps to under J — independent of rotation θ. ARAP penalises deviation from (1,1); ACAP penalises deviation from σ_max = σ_min.
UV parameterization = per-face J
Unwrapping a 3D mesh assigns a J to every triangle. Minimising ARAP energy globally gives near-isometric UV maps; minimising ACAP gives angle-preserving (conformal) maps.
The one-sentence summary: project each 3D triangle to a local 2D frame (Part 1), compute $J = M_t M_s^{-1}$ (Part 2), then read off $\sigma_{\max}$ and $\sigma_{\min}$ from the SVD to measure how much the triangle was stretched and sheared (Part 3) — that is the complete distortion pipeline used in ARAP, ACAP, and UV parameterization solvers.