Planes: point and normal, ax + by + cz + d
A plane is a flat surface that goes on for ever: a floor level, a wall face, a roof slope, a section cut. Store it as a point and a normal, or as four numbers a, b, c and d, and build one from three points.
Point and normal
The equation ax + by + cz + d = 0
(a, b, c) is the direction the plane faces. |d| is the plane’s distance from the origin, and −d·n is its closest point to the origin (P0 in the widget). Putting any point into ax + by + cz + d gives its signed distance from the plane. That is the subject of the next lesson.
| Plane | Unit normal (a, b, c) | d | Equation |
|---|---|---|---|
A plane from three points
var a = new V3(0, 0, 3); // eaves, one endvar b = new V3(10, 0, 3); // eaves, other endvar c = new V3(0, 4, 6); // a point on the ridge V3 n = V3.Cross(b - a, c - a).Normalized();double d = -V3.Dot(n, a);Console.WriteLine($"n = {n}, d = {d:0.###}");Console.WriteLine($"Pitch {Math.Acos(n.Z) * 180 / Math.PI:F2} deg"); foreach (var q in new V3[] { new(5, 2, 4.5), new(5, 2, 5) }){ double s = V3.Dot(n, q) + d; Console.WriteLine($"{q}: {(Math.Abs(s) < 1e-9 ? "on the roof plane" : "off the plane")}");} 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); public override string ToString() => $"({X:0.###}, {Y:0.###}, {Z:0.###})";}n = (0, -0.6, 0.8), d = -2.4 Pitch 36.87 deg (5, 2, 4.5): on the roof plane (5, 2, 5): off the plane
Which way does the normal point?
Show a hint
Show the solution
var p0 = new V3(2, 0, 0);V3 n = new V3(1, 1, 0).Normalized();double d = -V3.Dot(n, p0);Console.WriteLine($"n = {n}, d = {d:0.###}"); foreach (var q in new V3[] { new(0, 2, 5), new(2, 2, 0) }){ double s = V3.Dot(n, q) + d; Console.WriteLine(Math.Abs(s) < 1e-6 ? $"{q}: on the plane" : $"{q}: {s:0.###} m off");} 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); public override string ToString() => $"({X:0.###}, {Y:0.###}, {Z:0.###})";}n = (0.707, 0.707, 0), d = -1.414 (0, 2, 5): on the plane (2, 2, 0): 1.414 m off
Remember
A plane is a point P0 and a normal n; X is on it when (X − P0)·n = 0. Expanded, that is ax + by + cz + d = 0 with (a, b, c) = n and d = −n·P0. With a unit normal, |d| is the distance from the origin and ax + by + cz + d is a signed distance. Three points give a plane: n = (B − A) × (C − A), normalised; check for collinear points first. n and −n give the same plane; by convention, face normals point out of the solid.