<style> .reveal-up, .reveal-left, .reveal-right, .reveal-stagger > * { opacity: 1 !important; transform: none !important; } </style>
  1. Learn
  2. BIM Math
  3. Matrices and transforms
All lessons in this course

Lesson 25 of 44 15 minAdvanced

Rotation in 2D, 3D and about any axis

Rotate a point in the plane with cos and sin, rotate about a pivot by moving it to the origin first, and in 3D rotate about X, Y, Z or any axis with Rodrigues’ formula.

Rotating a building footprint to true north, swinging a door, turning a family instance, tilting a window about its sill: rotation is the transform BIM software uses most. In 2D it is one angle. In 3D it needs an axis as well, and the direction of that axis decides which way “positive” turns.

Interactive

Rotation in 2D and 3D

Drag the pivot C. The dashed outline is where the shape started.

Q′QC
cos θ0.500
sin θ0.866
R[0.500 −0.866]
[0.866 0.500]
Q − C(1.8, 2.6)
Q′ = C + R(Q − C)(−0.35, 3.86)
Every point turns 60° counterclockwise round C, and keeps its distance from C. Rotating about a pivot is three steps: move C to the origin, rotate, move back. Only C itself stays put.

Rotation in 2D

Use the columns idea from the last lesson. Turning by θ sends the X axis to (cos θ, sin θ) and the Y axis to (−sin θ, cos θ), a quarter turn further on. Those are the two columns, so:

x′ = x·cos θ − y·sin θ and y′ = x·sin θ + y·cos θ.

A positive angle turns counterclockwise when you look down on the XY plane with Z pointing at you. Plans are drawn that way, which is why a positive rotation in most BIM tools turns an instance counterclockwise in plan.

Rotating about a pivot

The formula turns points about the origin. To turn about another point C, such as a building corner or a door hinge, move C to the origin, rotate, and move back: P′ = C + R·(P − C). In the widget, drag C and watch Q circle round it.

RotateAbout.cs
var pivot = new V2(2, 3);                    // the building corner that stays putvar corners = new V2[] { new(2, 3), new(12, 3), new(12, 9), new(2, 9) }; foreach (var c in corners)    Console.WriteLine($"{c} -> {RotateAbout(c, pivot, 30)}"); static V2 Rotate(V2 v, double degrees){    double t = degrees * Math.PI / 180, c = Math.Cos(t), s = Math.Sin(t);    return new(v.X * c - v.Y * s, v.X * s + v.Y * c);} static V2 RotateAbout(V2 p, V2 pivot, double degrees) => pivot + Rotate(p - pivot, degrees); readonly record struct V2(double X, double Y){    public static V2 operator +(V2 a, V2 b) => new(a.X + b.X, a.Y + b.Y);    public static V2 operator -(V2 a, V2 b) => new(a.X - b.X, a.Y - b.Y);    public static V2 operator *(V2 v, double k) => new(v.X * k, v.Y * k);    public double Length => Math.Sqrt(X * X + Y * Y);    static string F(double v) => Math.Abs(v) < 5e-4 ? "0" : v.ToString("0.###");    public override string ToString() => $"({F(X)}, {F(Y)})";}
Output
(2, 3) -> (2, 3)
(12, 3) -> (10.66, 8)
(12, 9) -> (7.66, 13.196)
(2, 9) -> (-1, 8.196)

Rotation in 3D about X, Y and Z

Rotating about the Z axis is the 2D rotation with z left alone. Rotating about X or Y works the same way in the other two planes. The rows of each 3×3 matrix are:

AboutRow 1Row 2Row 3Turns
X1, 0, 00, cos θ, −sin θ0, sin θ, cos θY towards Z
Ycos θ, 0, sin θ0, 1, 0−sin θ, 0, cos θZ towards X
Zcos θ, −sin θ, 0sin θ, cos θ, 00, 0, 1X towards Y

The minus sign sits in a different place for Y because the cycle runs X → Y → Z → X: turning about Y takes Z towards X, not X towards Z. All three follow the right-hand rule: point your right thumb along the axis, and your fingers curl the way a positive angle turns.

Rotation about any axis

A hinge is rarely exactly on a coordinate axis. To rotate a vector v by θ about a unit axis k, split v into a part along k, which does not move, and a part at right angles to k, which turns like a 2D rotation. The result is Rodrigues’ formula:

v′ = v·cos θ + (k × v)·sin θ + k·(k·v)·(1 − cos θ)

As with the pivot, an axis that does not pass through the origin is handled by subtracting a point on it first and adding it back afterwards.

RotateAboutAxis.cs
// A tilt-and-turn window tilts about its sill: an axis along X, through (0, 0, 1)var sillPoint = new V3(0, 0, 1);var sillDir = new V3(1, 0, 0);var topCorner = new V3(1.2, 0, 2.4);Console.WriteLine($"Top corner tilted 10 deg: {RotateAbout(topCorner, sillPoint, sillDir, 10)}"); // 120 deg about (1, 1, 1) cycles the axes: X -> Y -> Z -> Xvar diagonal = new V3(1, 1, 1).Normalized();foreach (var v in new V3[] { new(1, 0, 0), new(0, 1, 0), new(0, 0, 1) })    Console.WriteLine($"{v} -> {Rotate(v, diagonal, 120)}"); static V3 Rotate(V3 v, V3 k, double degrees)          // Rodrigues; k must be a unit vector{    double t = degrees * Math.PI / 180, c = Math.Cos(t), s = Math.Sin(t);    return v * c + V3.Cross(k, v) * s + k * (V3.Dot(k, v) * (1 - c));} static V3 RotateAbout(V3 p, V3 pointOnAxis, V3 axis, double degrees) =>    pointOnAxis + Rotate(p - pointOnAxis, axis.Normalized(), degrees); readonly record struct V3(double X, double Y, double Z){    public static V3 operator +(V3 a, V3 b) => new(a.X + b.X, a.Y + b.Y, a.Z + b.Z);    public static V3 operator -(V3 a, V3 b) => new(a.X - b.X, a.Y - b.Y, a.Z - b.Z);    public static V3 operator *(V3 v, double k) => new(v.X * k, v.Y * k, v.Z * k);    public static double Dot(V3 a, V3 b) => a.X * b.X + a.Y * b.Y + a.Z * b.Z;    public static V3 Cross(V3 a, V3 b) =>        new(a.Y * b.Z - a.Z * b.Y, a.Z * b.X - a.X * b.Z, a.X * b.Y - a.Y * b.X);    public double Length => Math.Sqrt(Dot(this, this));    public V3 Normalized() => this * (1 / Length);    static string F(double v) => Math.Abs(v) < 5e-4 ? "0" : v.ToString("0.###");    public override string ToString() => $"({F(X)}, {F(Y)}, {F(Z)})";}
Output
Top corner tilted 10 deg: (1.2, -0.243, 2.379)
(1, 0, 0) -> (0, 1, 0)
(0, 1, 0) -> (0, 0, 1)
(0, 0, 1) -> (1, 0, 0)

The top of the window moves to −Y as it tilts: by the right-hand rule, a positive turn about +X carries +Z towards −Y. If the room is on the +Y side, tilt by −10° instead, or reverse the axis direction.

What every rotation matrix has in common

  • Its columns are unit vectors at right angles to each other (the turned axes). Such a matrix is called orthonormal.
  • Lengths and angles do not change, and det = +1.
  • Its inverse is its transpose Rᵀ: swap rows and columns to undo the turn, with no division needed.
Quick check

Rotating (1, 0) by 90° counterclockwise gives…

Quick check

How do you rotate a door leaf about its hinge H?

Practice

Build the 3×3 matrix for a 40° turn about the axis (0, 0.6, 0.8) by rotating the three unit axes with Rodrigues’ formula (they are its columns). Check that the columns have length 1, are at right angles to each other, and that det = (X × Y)·Z is 1.

Show a hint

Reuse Rotate from RotateAboutAxis.cs. The axis (0, 0.6, 0.8) already has length 1.

Show the solution
Solution.cs
var k = new V3(0, 0.6, 0.8);V3 x = Rotate(new V3(1, 0, 0), k, 40);V3 y = Rotate(new V3(0, 1, 0), k, 40);V3 z = Rotate(new V3(0, 0, 1), k, 40);Console.WriteLine($"Columns: {x} {y} {z}"); bool unit = new[] { x, y, z }.All(c => Math.Abs(c.Length - 1) < 1e-12);bool square = Math.Abs(V3.Dot(x, y)) < 1e-12 && Math.Abs(V3.Dot(y, z)) < 1e-12 && Math.Abs(V3.Dot(z, x)) < 1e-12;Console.WriteLine($"Unit columns: {unit}, at right angles: {square}");Console.WriteLine($"det = {V3.Dot(V3.Cross(x, y), z):F6}"); static V3 Rotate(V3 v, V3 k, double degrees){    double t = degrees * Math.PI / 180, c = Math.Cos(t), s = Math.Sin(t);    return v * c + V3.Cross(k, v) * s + k * (V3.Dot(k, v) * (1 - c));} readonly record struct V3(double X, double Y, double Z){    public static V3 operator +(V3 a, V3 b) => new(a.X + b.X, a.Y + b.Y, a.Z + b.Z);    public static V3 operator -(V3 a, V3 b) => new(a.X - b.X, a.Y - b.Y, a.Z - b.Z);    public static V3 operator *(V3 v, double k) => new(v.X * k, v.Y * k, v.Z * k);    public static double Dot(V3 a, V3 b) => a.X * b.X + a.Y * b.Y + a.Z * b.Z;    public static V3 Cross(V3 a, V3 b) =>        new(a.Y * b.Z - a.Z * b.Y, a.Z * b.X - a.X * b.Z, a.X * b.Y - a.Y * b.X);    public double Length => Math.Sqrt(Dot(this, this));    public V3 Normalized() => this * (1 / Length);    static string F(double v) => Math.Abs(v) < 5e-4 ? "0" : v.ToString("0.###");    public override string ToString() => $"({F(X)}, {F(Y)}, {F(Z)})";}
Output
Columns: (0.766, 0.514, -0.386) (-0.514, 0.85, 0.112) (0.386, 0.112, 0.916)
Unit columns: True, at right angles: True
det = 1.000000

Remember

  • 2D: x′ = x·cos θ − y·sin θ, y′ = x·sin θ + y·cos θ; positive is counterclockwise in plan.
  • To rotate about a pivot C: P′ = C + R·(P − C).
  • In 3D, rotations about X, Y and Z follow the right-hand rule; the cycle X → Y → Z puts the minus sign of the Y matrix in a different place.
  • Rodrigues’ formula rotates about any unit axis: v·cos θ + (k × v)·sin θ + k·(k·v)·(1 − cos θ).
  • Rotation matrices are orthonormal with det = 1, and their inverse is their transpose.
0 of 44 lessons completed