Determining if a point is inside a polyhedron

前端 未结 3 1456
粉色の甜心
粉色の甜心 2021-02-10 02:03

I\'m attempting to determine if a specific point lies inside a polyhedron. In my current implementation, the method I\'m working on take the point we\'re looking for an array of

相关标签:
3条回答
  • 2021-02-10 02:15

    The link in your question has expired and I could not understand the algorithm from your code. Assuming you have a convex polyhedron with counterclockwise oriented faces (seen from outside), it should be sufficient to check that your point is behind all faces. To do that, you can take the vector from the point to each face and check the sign of the scalar product with the face's normal. If it is positive, the point is behind the face; if it is zero, the point is on the face; if it is negative, the point is in front of the face.

    Here is some complete C++11 code, that works with 3-point faces or plain more-point faces (only the first 3 points are considered). You can easily change bound to exclude the boundaries.

    #include <vector>
    #include <cassert>
    #include <iostream>
    #include <cmath>
    
    struct Vector {
      double x, y, z;
    
      Vector operator-(Vector p) const {
        return Vector{x - p.x, y - p.y, z - p.z};
      }
    
      Vector cross(Vector p) const {
        return Vector{
          y * p.z - p.y * z,
          z * p.x - p.z * x,
          x * p.y - p.x * y
        };
      }
    
      double dot(Vector p) const {
        return x * p.x + y * p.y + z * p.z;
      }
    
      double norm() const {
        return std::sqrt(x*x + y*y + z*z);
      }
    };
    
    using Point = Vector;
    
    struct Face {
      std::vector<Point> v;
    
      Vector normal() const {
        assert(v.size() > 2);
        Vector dir1 = v[1] - v[0];
        Vector dir2 = v[2] - v[0];
        Vector n  = dir1.cross(dir2);
        double d = n.norm();
        return Vector{n.x / d, n.y / d, n.z / d};
      }
    };
    
    bool isInConvexPoly(Point const& p, std::vector<Face> const& fs) {
      for (Face const& f : fs) {
        Vector p2f = f.v[0] - p;         // f.v[0] is an arbitrary point on f
        double d = p2f.dot(f.normal());
        d /= p2f.norm();                 // for numeric stability
    
        constexpr double bound = -1e-15; // use 1e15 to exclude boundaries
        if (d < bound)
          return false;
      }
    
      return true;
    }
    
    int main(int argc, char* argv[]) {
      assert(argc == 3+1);
      char* end;
      Point p;
      p.x = std::strtod(argv[1], &end);
      p.y = std::strtod(argv[2], &end);
      p.z = std::strtod(argv[3], &end);
    
      std::vector<Face> cube{ // faces with 4 points, last point is ignored
        Face{{Point{0,0,0}, Point{1,0,0}, Point{1,0,1}, Point{0,0,1}}}, // front
        Face{{Point{0,1,0}, Point{0,1,1}, Point{1,1,1}, Point{1,1,0}}}, // back
        Face{{Point{0,0,0}, Point{0,0,1}, Point{0,1,1}, Point{0,1,0}}}, // left
        Face{{Point{1,0,0}, Point{1,1,0}, Point{1,1,1}, Point{1,0,1}}}, // right
        Face{{Point{0,0,1}, Point{1,0,1}, Point{1,1,1}, Point{0,1,1}}}, // top
        Face{{Point{0,0,0}, Point{0,1,0}, Point{1,1,0}, Point{1,0,0}}}, // bottom
      };
    
      std::cout << (isInConvexPoly(p, cube) ? "inside" : "outside") << std::endl;
    
      return 0;
    }
    

    Compile it with your favorite compiler

    clang++ -Wall -std=c++11 code.cpp -o inpoly
    

    and test it like

    $ ./inpoly 0.5 0.5 0.5
    inside
    $ ./inpoly 1 1 1
    inside
    $ ./inpoly 2 2 2
    outside
    
    0 讨论(0)
  • 2021-02-10 02:21

    If your mesh is concave, and not necessarily watertight, that’s rather hard to accomplish.

    As a first step, find the point on the surface of the mesh closest to the point. You need to keep track the location, and specific feature: whether the closest point is in the middle of face, on the edge of the mesh, or one of the vertices of the mesh.

    If the feature is face, you’re lucky, can use windings to find whether it’s inside or outside. Compute normal to face (don't even need to normalize it, non-unit-length will do), then compute dot( normal, pt - tri[0] ) where pt is your point, tri[0] is any vertex of the face. If the faces have consistent winding, the sign of that dot product will tell you if it’s inside or outside.

    If the feature is edge, compute normals to both faces (by normalizing a cross-product), add them together, use that as a normal to the mesh, and compute the same dot product.

    The hardest case is when a vertex is the closest feature. To compute mesh normal at that vertex, you need to compute sum of the normals of the faces sharing that vertex, weighted by 2D angles of that face at that vertex. For example, for vertex of cube with 3 neighbor triangles, the weights will be Pi/2. For vertex of a cube with 6 neighbor triangles the weights will be Pi/4. And for real-life meshes the weights will be different for each face, in the range [ 0 .. +Pi ]. This means you gonna need some inverse trigonometry code for this case to compute the angle, probably acos().

    If you want to know why that works, see e.g. “Generating Signed Distance Fields From Triangle Meshes” by J. Andreas Bærentzen and Henrik Aanæs.

    0 讨论(0)
  • 2021-02-10 02:26

    It turns out that the problem was my reading of the algorithm referenced in the link above. I was reading:

    N = - dot product of (P0-Vi) and ni;
    

    as

    N = - dot product of S and ni;
    

    Having changed this, the code above now seems to work correctly. (I'm also updating the code in the question to reflect the correct solution).

    0 讨论(0)
提交回复
热议问题