If I have a square based pyramid of which I know every point (all 5), what is the algorithm to determine if a given point is inside that pyramid or not? I saw a similar question here Check if a point is inside a rectangular shaped area (3D)? and here How do I find if a point exists in a3D solid?. I tried to change the math a bit but didn't work out for me (maybe because I don't understand some parts correctly).
I have seen approaches with creating a normal to each plane (4 triangles + 1 square) and checking on which side of the plane the point lies… however I do not know the correct math for that.
If someone could help me with the exact math for that (preferably with explanation), it would really help.
Python "Example":
Input:
# Pyarmid Points
A = np.array([2, 2, 4]) # pyramid top
B = np.array([1, 1, 1])
C = np.array([1, 3, 3])
D = np.array([3, 3, 1])
E = np.array([3, 1, 1])
# Example points
point_inside = np.array([2, 2, 2])
point_outside = np.array([3, 3, 3])
Expectation:
def check_point_in_pyramid(A, B, C, D, E, Point):
# math...
if point_inside_pyramid:
return true
return false
So for point_inside the function return would be true and for the point_outside it would be false.
I do not need the python code, I just added it for clarification purposes what I need. The math is enough.
Edit after I read the answers:
I posted this question on stackoverflow too. Below are more deep level explanations and as far as I could tell a different approach to some extent. Definitely interesting. On stackoverflow are more lightweight answer for those looking. https://stackoverflow.com/questions/68641598/check-if-a-3d-point-is-in-a-square-based-pyramid-or-not
Best Answer
All convex polyhedra can be described as the intersection of four or more half-spaces. We describe a half-space using a plane.
If $\vec{n} = (n_x, n_y, n_z)$ is the normal of the plane, and $d$ is the signed distance from the plane to origin, then point $\vec{p} = (x, y, z)$ is within the half-space if and only if $$\vec{p} \cdot \vec{n} \ge d \tag{1a}\label{BtV1a}$$ which is equivalent to $$x n_x + y n_y + z n_z \ge d \tag{1b}\label{BtV1b}$$ in Cartesian coordinates.
To define a plane or a half-space ($\vec{n}$ and $d$), you can use three points (three consecutive vertices of a polyhedron side polygon): $\vec{v}_1 = (x_1, y_1, z_1)$, $\vec{v}_2 = (x_2, y_2, z_2)$, $\vec{v}_3 = (x_3, y_3, z_3)$: $$\left\lbrace \, \begin{aligned} \vec{n} & = (v_1 - v_2) \times (v_3 - v_2) \\ d & = \vec{n} \cdot \vec{v}_1 = \vec{n} \cdot \vec{v}_2 = \vec{n} \cdot \vec{v}_3 \\ \end{aligned} \right. \tag{2a}\label{BtV2a}$$ where $\times$ is the vector cross product, and you can choose any of the three right sides for $d$ (because they are equivalent). This is equivalent to $$\left\lbrace \, \begin{aligned} n_x &= (y_1 - y_2)(z_3 - z_2) - (z_1 - z_2)(y_3 - y_2) \\ n_y &= (z_1 - z_2)(x_3 - x_2) - (x_1 - x_2)(z_3 - z_2) \\ n_z &= (x_1 - x_2)(y_3 - y_2) - (y_1 - y_2)(x_3 - x_2) \\ d &= n_x x_1 + n_y y_1 + n_z z_1 = n_x x_2 + n_y y_2 + n_z z_2 = n_x x_3 + n_y y_3 + n_z z_3 \\ \end{aligned} \right . \tag{2b}\label{BtV2b}$$ in Cartesian coordinates.
When working with floating-point numbers, you should use maximum of the three right sides for the signed distance $d$, $$d = \max\bigl( n_x x_1 + n_y y_1 + n_z z_1 ,\, n_x x_2 + n_y y_2 + n_z z_2 ,\, n_x x_3 + n_y y_3 + n_z z_3 \bigr)$$ to minimize rounding errors. (For the base of the pyramid, you'll want to use the fourth planar vertex here also, to ensure it too is considered "inside" the pyramid.)
Using the notation in $\eqref{BtV1a}$ and $\eqref{BtV1b}$, the plane normal points to "inside". Depending on the order of the three points above, you can get $\vec{n}$ that points "inside" or "outside". If you know you have point $\vec{p} = (x, y, z)$ "inside" the half-space, just calculate $$\vec{p} \cdot \vec{n} - d = x n_x + y n_y + z n_z - d \tag{3}\label{BtV3}$$ If this is negative, you need to negate both $\vec{n}$ and $d$ (i.e, negate all four scalars $n_x$, $n_y$, $n_z$, and $d$). Then, you can be sure $\vec{n}$ points "inside" the half-space.
In your program, you need to calculate the plane/half-space for each side of the rectangular pyramid; five in total. You can use either the centroid of the vertices (average of the five vertex coordinates) –– because the pyramid is convex, this is always inside it ––, or one of the vertices to verify your normals all point "inside".
Here is an example Python 3 implementation,
pyramid.py
:Note that the vertices for the base must be listed in (cyclic) order, i.e. tracing the vertices in order as one travels the perimeter of the face. If you specify them in "butterfly" order, you'll get very odd results.
For example, to construct a pyramid
P
with base $(-1,0,0)$, $(0,-1,0)$, $(1,0,0)$, $(0,1,0)$, and apex at $(0,0,1)$, and to check if point $(0,0,0.5)$ is inside the pyramid, use (in the same directory as abovepyramid.py
)In general, this half-space based test tends to be the most efficient test for convex polyhedra. (When there are many faces, it is often useful to define one or more spheres that are completely inside the polyhedron, and/or one (or more) that contain the polyhedron. This is because point-in-sphere test is even more trivial; the former allow shortcuts to "inside", and the latter to "outside".)
For this pyramid case, the test involves 15 multiplications, 10 additions, and 5 comparisons (between floating-point scalars), which is quite efficient.