I'm choosing 4 points from an image outputted by Kinect therefor each point has its (x, y, z)
coordinates.
My objective is to determine whether the 4 points fall on the same plane.
This is my function:
public bool isValidPlane()
{
for (int i = 0; i < edgesPoints.Length; i++)
{
double absPlaneEquation = Math.Abs(distance -
(normal.X * edgesPoints[i].X + normal.Y * edgesPoints[i].Y + normal.Z * edgesPoints[i].Z));
if (absPlaneEquation > 1500) /* 1500 is a tolerance error*/
{
return false;
}
}
return true;
}
The normal
is also the normal (a cross product of 2 vectors on the plane, which been previously calculated from 3 out of 4 of the chosen points) to the plane and it's normalized:
private void calcPlaneNormalVector()
{
if (lastEdgeNumber < 3)
{
return;
}
Vector3D vec1 = new Vector3D(edgesPoints[0], edgesPoints[1]);
Vector3D vec2 = new Vector3D(edgesPoints[0], edgesPoints[2]);
vec2 = vec1.crossProduct(vec2);
double lengthNormal = Math.Sqrt(Math.Pow(vec2.X, 2) + Math.Pow(vec2.Y, 2) + Math.Pow(vec2.Z, 2));
//normalizing:
normal = new Vector3D((vec2.X / lengthNormal), (vec2.Y / lengthNormal), (vec2.Z / lengthNormal));
distance = (-1) * (edgesPoints[0].X * normal.X + edgesPoints[0].Y * normal.Y + edgesPoints[0].Z + normal.Z);
}
Vector3D
is a class to represent a vector:
public class Vector3D
{
private double x, y, z;
public Vector3D(Point3D p1, Point3D p2)
{
x = p2.X - p1.X;
y = p2.Y - p1.Y;
z = p2.Z - p1.Z;
}
public Vector3D(double a = 0, double b = 0, double c = 0)
{
x = a;
y = b;
z = c;
}
<get properties for x, y, z >
public Vector3D crossProduct(Vector3D u)
{
double tmpX = 0, tmpY = 0, tmpZ = 0;
tmpX = y * u.Z - z * u.Y;
tmpY = z * u.X - x * u.Z;
tmpZ = x * u.Y - y * u.X;
return new Vector3D(tmpX, tmpY, tmpZ);
}
public double dotProduct(Vector3D u)
{
return x * u.X + y * u.Y + z * u.Z;
}
}
i always get 1300 <= absPlaneEquation <= 1400
even when the 4 points are chosen so that they won't be on the same plane.
What is the best way to detect that the 4 points refer to the same plane?
If θ = 0º or 180º, the cross-product will be 0, and all 4 points lie on the same plane. Use the three points to find its equation ( that is find A,B, and C ) , then plug in the 4th point to see if that satisfies then they are all in the same plane.
Put the coordinates of the given points in the equation of plane and store the values in variables P1 and P2. Check the sign of the obtained values: If P1 and P2 have the same parity, then they are on the same side of the plane. If P1 and P2 have different parity then they lie on the opposite sides of the plane.
Four points (like the corners of a tetrahedron or a triangular pyramid) will not all be on any plane, though triples of them will form four different planes. Stepping down, two points form a line, and there wil be a fan of planes with this line (like pages of an open book, with the line down the spine of the book).
Once you have the normal vector of a plane, you can evaluate the plane's equation:
normal vector components : [A, B, C]
Plane equation : A·x + B·y + C·z + D = 0;
Use one of the three points (P1
, P2
or P3
) used to obtain the normal vector to evaluate D
and then simply check that the fourth point (P4
) satisfies the equation:
D = - (A·x1 + B·y1 + C·z1)
A·x4 + B·y4 + C·z4 - (A·x1 + B·y1 + C·z1) = 0
It is importat to note that you are using floating point arithmetics so you can not test for strict equality. You need to define an acceptable error and check that the fourth points complies with the equation according to such tolerance:
|A·x4 + B·y4 + C·z4 - (A·x1 + B·y1 + C·z1)| < TOLERANCE
UPDATE: Here's how I'd code the solution to your problem:
public struct Point3D
{
public double X { get; }
public double Y { get; }
public double Z { get; }
public Point3D(double x, double y, double z)
{
X = x;
Y = y;
Z = z;
}
}
public struct Vector3D
{
public double X { get; }
public double Y { get; }
public double Z { get; }
public double Magnitude => Math.Sqrt(X * X + Y * Y + Z * Z);
public Vector3D(Point3D p1, Point3D p2)
: this(p2.X - p1.X, p2.Y - p1.Y, p2.Z - p1.Z)
{
}
public Vector3D(double x, double y, double z)
{
X = x;
Y = y;
Z = z;
}
public static Vector3D CrossProduct(Vector3D left, Vector3D right)
{
double tmpX = 0, tmpY = 0, tmpZ = 0;
tmpX = left.Y * right.Z - left.Z * right.Y;
tmpY = left.Z * right.X - left.X * right.Z;
tmpZ = left.X * right.Y - left.Y * right.X;
return new Vector3D(tmpX, tmpY, tmpZ);
}
public static double DotProduct(Vector3D left, Vector3D right)
{
return left.X * right.X + left.Y * right.Y + left.Z * right.Z;
}
}
public struct Plane3D
{
private const double TOLERANCE = 0.001;
private readonly double independentTerm;
public Vector3D Normal { get; }
public Plane3D(Point3D p1, Point3D p2, Point3D p3)
{
Normal = Vector3D.crossProduct(new Vector3D(p1, p2), new Vector3D(p1, p3));
if (Normal.Magnitude < TOLERANCE)
throw new ArgumentException("Specified points do not define a valid plane.");
independentTerm = -(Normal.X * p1.X + Normal.Y * p1.Y + Normal.Z * p1.Z);
}
public bool Contains(Point3D p) => Math.Abs(Normal.X * p.X + Normal.Y * p.Y + Normal.Z * p.Z + independentTerm) < TOLERANCE;
}
Things to note:
Point3D
and Vector3D
to structs. This largely depends on how you will use the objects but, at first glance, value types seem a better fit.static
methods. This could be down to personal taste.TOLERANCE
const inside Plane
. There is probably much better places where to define it, its simply there for convenience.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With