123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575 |
- // David Eberly, Geometric Tools, Redmond WA 98052
- // Copyright (c) 1998-2020
- // Distributed under the Boost Software License, Version 1.0.
- // https://www.boost.org/LICENSE_1_0.txt
- // https://www.geometrictools.com/License/Boost/LICENSE_1_0.txt
- // Version: 4.0.2019.08.13
- #pragma once
- #include <Mathematics/Logger.h>
- #include <Mathematics/ContPointInPolygon2.h>
- #include <Mathematics/IntrRay3Plane3.h>
- #include <Mathematics/IntrRay3Triangle3.h>
- #include <vector>
- // This class contains various implementations for point-in-polyhedron
- // queries. The planes stored with the faces are used in all cases to
- // reject ray-face intersection tests, a quick culling operation.
- //
- // The algorithm is to cast a ray from the input point P and test for
- // intersection against each face of the polyhedron. If the ray only
- // intersects faces at interior points (not vertices, not edge points),
- // then the point is inside when the number of intersections is odd and
- // the point is outside when the number of intersections is even. If the
- // ray intersects an edge or a vertex, then the counting must be handled
- // differently. The details are tedious. As an alternative, the approach
- // here is to allow you to specify 2*N+1 rays, where N >= 0. You should
- // choose these rays randomly. Each ray reports "inside" or "outside".
- // Whichever result occurs N+1 or more times is the "winner". The input
- // rayQuantity is 2*N+1. The input array Direction must have rayQuantity
- // elements. If you are feeling lucky, choose rayQuantity to be 1.
- namespace WwiseGTE
- {
- template <typename Real>
- class PointInPolyhedron3
- {
- public:
- // For simple polyhedra with triangle faces.
- class TriangleFace
- {
- public:
- // When you view the face from outside, the vertices are
- // counterclockwise ordered. The indices array stores the indices
- // into the vertex array.
- std::array<int, 3> indices;
- // The normal vector is unit length and points to the outside of
- // the polyhedron.
- Plane3<Real> plane;
- };
- // The Contains query will use ray-triangle intersection queries.
- PointInPolyhedron3(int numPoints, Vector3<Real> const* points,
- int numFaces, TriangleFace const* faces, int numRays,
- Vector3<Real> const* directions)
- :
- mNumPoints(numPoints),
- mPoints(points),
- mNumFaces(numFaces),
- mTFaces(faces),
- mCFaces(nullptr),
- mSFaces(nullptr),
- mMethod(0),
- mNumRays(numRays),
- mDirections(directions)
- {
- }
- // For simple polyhedra with convex polygon faces.
- class ConvexFace
- {
- public:
- // When you view the face from outside, the vertices are
- // counterclockwise ordered. The indices array stores the indices
- // into the vertex array.
- std::vector<int> indices;
- // The normal vector is unit length and points to the outside of
- // the polyhedron.
- Plane3<Real> plane;
- };
- // The Contains() query will use ray-convexpolygon intersection
- // queries. A ray-convexpolygon intersection query can be implemented
- // in many ways. In this context, uiMethod is one of three value:
- // 0 : Use a triangle fan and perform a ray-triangle intersection
- // query for each triangle.
- // 1 : Find the point of intersection of ray and plane of polygon.
- // Test whether that point is inside the convex polygon using an
- // O(N) test.
- // 2 : Find the point of intersection of ray and plane of polygon.
- // Test whether that point is inside the convex polygon using an
- // O(log N) test.
- PointInPolyhedron3(int numPoints, Vector3<Real> const* points,
- int numFaces, ConvexFace const* faces, int numRays,
- Vector3<Real> const* directions, unsigned int method)
- :
- mNumPoints(numPoints),
- mPoints(points),
- mNumFaces(numFaces),
- mTFaces(nullptr),
- mCFaces(faces),
- mSFaces(nullptr),
- mMethod(method),
- mNumRays(numRays),
- mDirections(directions)
- {
- }
- // For simple polyhedra with simple polygon faces that are generally
- // not all convex.
- class SimpleFace
- {
- public:
- // When you view the face from outside, the vertices are
- // counterclockwise ordered. The Indices array stores the indices
- // into the vertex array.
- std::vector<int> indices;
- // The normal vector is unit length and points to the outside of
- // the polyhedron.
- Plane3<Real> plane;
- // Each simple face may be triangulated. The indices are relative
- // to the vertex array. Each triple of indices represents a
- // triangle in the triangulation.
- std::vector<int> triangles;
- };
- // The Contains query will use ray-simplepolygon intersection queries.
- // A ray-simplepolygon intersection query can be implemented in a
- // couple of ways. In this context, uiMethod is one of two value:
- // 0 : Iterate over the triangles of each face and perform a
- // ray-triangle intersection query for each triangle. This
- // requires that the SimpleFace::Triangles array be initialized
- // for each face.
- // 1 : Find the point of intersection of ray and plane of polygon.
- // Test whether that point is inside the polygon using an O(N)
- // test. The SimpleFace::Triangles array is not used for this
- // method, so it does not have to be initialized for each face.
- PointInPolyhedron3(int numPoints, Vector3<Real> const* points,
- int numFaces, SimpleFace const* faces, int numRays,
- Vector3<Real> const* directions, unsigned int method)
- :
- mNumPoints(numPoints),
- mPoints(points),
- mNumFaces(numFaces),
- mTFaces(nullptr),
- mCFaces(nullptr),
- mSFaces(faces),
- mMethod(method),
- mNumRays(numRays),
- mDirections(directions)
- {
- }
- // This function will select the actual algorithm based on which
- // constructor you used for this class.
- bool Contains(Vector3<Real> const& p) const
- {
- if (mTFaces)
- {
- return ContainsT0(p);
- }
- if (mCFaces)
- {
- if (mMethod == 0)
- {
- return ContainsC0(p);
- }
- return ContainsC1C2(p, mMethod);
- }
- if (mSFaces)
- {
- if (mMethod == 0)
- {
- return ContainsS0(p);
- }
- if (mMethod == 1)
- {
- return ContainsS1(p);
- }
- }
- return false;
- }
- private:
- // For all types of faces. The ray origin is the test point. The ray
- // direction is one of those passed to the constructors. The plane
- // origin is a point on the plane of the face. The plane normal is a
- // unit-length normal to the face and that points outside the
- // polyhedron.
- static bool FastNoIntersect(Ray3<Real> const& ray, Plane3<Real> const& plane)
- {
- Real planeDistance = Dot(plane.normal, ray.origin) - plane.constant;
- Real planeAngle = Dot(plane.normal, ray.direction);
- if (planeDistance < (Real)0)
- {
- // The ray origin is on the negative side of the plane.
- if (planeAngle <= (Real)0)
- {
- // The ray points away from the plane.
- return true;
- }
- }
- if (planeDistance > (Real)0)
- {
- // The ray origin is on the positive side of the plane.
- if (planeAngle >= (Real)0)
- {
- // The ray points away from the plane.
- return true;
- }
- }
- return false;
- }
- // For triangle faces.
- bool ContainsT0(Vector3<Real> const& p) const
- {
- int insideCount = 0;
- TIQuery<Real, Ray3<Real>, Triangle3<Real>> rtQuery;
- Triangle3<Real> triangle;
- Ray3<Real> ray;
- ray.origin = p;
- for (int j = 0; j < mNumRays; ++j)
- {
- ray.direction = mDirections[j];
- // Zero intersections to start with.
- bool odd = false;
- TriangleFace const* face = mTFaces;
- for (int i = 0; i < mNumFaces; ++i, ++face)
- {
- // Attempt to quickly cull the triangle.
- if (FastNoIntersect(ray, face->plane))
- {
- continue;
- }
- // Get the triangle vertices.
- for (int k = 0; k < 3; ++k)
- {
- triangle.v[k] = mPoints[face->indices[k]];
- }
- // Test for intersection.
- if (rtQuery(ray, triangle).intersect)
- {
- // The ray intersects the triangle.
- odd = !odd;
- }
- }
- if (odd)
- {
- insideCount++;
- }
- }
- return insideCount > mNumRays / 2;
- }
- // For convex faces.
- bool ContainsC0(Vector3<Real> const& p) const
- {
- int insideCount = 0;
- TIQuery<Real, Ray3<Real>, Triangle3<Real>> rtQuery;
- Triangle3<Real> triangle;
- Ray3<Real> ray;
- ray.origin = p;
- for (int j = 0; j < mNumRays; ++j)
- {
- ray.direction = mDirections[j];
- // Zero intersections to start with.
- bool odd = false;
- ConvexFace const* face = mCFaces;
- for (int i = 0; i < mNumFaces; ++i, ++face)
- {
- // Attempt to quickly cull the triangle.
- if (FastNoIntersect(ray, face->plane))
- {
- continue;
- }
- // Process the triangles in a trifan of the face.
- size_t numVerticesM1 = face->indices.size() - 1;
- triangle.v[0] = mPoints[face->indices[0]];
- for (size_t k = 1; k < numVerticesM1; ++k)
- {
- triangle.v[1] = mPoints[face->indices[k]];
- triangle.v[2] = mPoints[face->indices[k + 1]];
- if (rtQuery(ray, triangle).intersect)
- {
- // The ray intersects the triangle.
- odd = !odd;
- }
- }
- }
- if (odd)
- {
- insideCount++;
- }
- }
- return insideCount > mNumRays / 2;
- }
- bool ContainsC1C2(Vector3<Real> const& p, unsigned int method) const
- {
- int insideCount = 0;
- FIQuery<Real, Ray3<Real>, Plane3<Real>> rpQuery;
- Ray3<Real> ray;
- ray.origin = p;
- for (int j = 0; j < mNumRays; ++j)
- {
- ray.direction = mDirections[j];
- // Zero intersections to start with.
- bool odd = false;
- ConvexFace const* face = mCFaces;
- for (int i = 0; i < mNumFaces; ++i, ++face)
- {
- // Attempt to quickly cull the triangle.
- if (FastNoIntersect(ray, face->plane))
- {
- continue;
- }
- // Compute the ray-plane intersection.
- auto result = rpQuery(ray, face->plane);
- // If you trigger this assertion, numerical round-off
- // errors have led to a discrepancy between
- // FastNoIntersect and the Find() result.
- LogAssert(result.intersect, "Unexpected condition.");
- // Get a coordinate system for the plane. Use vertex 0
- // as the origin.
- Vector3<Real> const& V0 = mPoints[face->indices[0]];
- Vector3<Real> basis[3];
- basis[0] = face->plane.normal;
- ComputeOrthogonalComplement(1, basis);
- // Project the intersection onto the plane.
- Vector3<Real> diff = result.point - V0;
- Vector2<Real> projIntersect{ Dot(basis[1], diff), Dot(basis[2], diff) };
- // Project the face vertices onto the plane of the face.
- if (face->indices.size() > mProjVertices.size())
- {
- mProjVertices.resize(face->indices.size());
- }
- // Project the remaining vertices. Vertex 0 is always the
- // origin.
- size_t numIndices = face->indices.size();
- mProjVertices[0] = Vector2<Real>::Zero();
- for (size_t k = 1; k < numIndices; ++k)
- {
- diff = mPoints[face->indices[k]] - V0;
- mProjVertices[k][0] = Dot(basis[1], diff);
- mProjVertices[k][1] = Dot(basis[2], diff);
- }
- // Test whether the intersection point is in the convex
- // polygon.
- PointInPolygon2<Real> PIP(static_cast<int>(mProjVertices.size()),
- &mProjVertices[0]);
- if (method == 1)
- {
- if (PIP.ContainsConvexOrderN(projIntersect))
- {
- // The ray intersects the triangle.
- odd = !odd;
- }
- }
- else
- {
- if (PIP.ContainsConvexOrderLogN(projIntersect))
- {
- // The ray intersects the triangle.
- odd = !odd;
- }
- }
- }
- if (odd)
- {
- insideCount++;
- }
- }
- return insideCount > mNumRays / 2;
- }
- // For simple faces.
- bool ContainsS0(Vector3<Real> const& p) const
- {
- int insideCount = 0;
- TIQuery<Real, Ray3<Real>, Triangle3<Real>> rtQuery;
- Triangle3<Real> triangle;
- Ray3<Real> ray;
- ray.origin = p;
- for (int j = 0; j < mNumRays; ++j)
- {
- ray.direction = mDirections[j];
- // Zero intersections to start with.
- bool odd = false;
- SimpleFace const* face = mSFaces;
- for (int i = 0; i < mNumFaces; ++i, ++face)
- {
- // Attempt to quickly cull the triangle.
- if (FastNoIntersect(ray, face->plane))
- {
- continue;
- }
- // The triangulation must exist to use it.
- size_t numTriangles = face->triangles.size() / 3;
- LogAssert(numTriangles > 0, "Triangulation must exist.");
- // Process the triangles in a triangulation of the face.
- int const* currIndex = &face->triangles[0];
- for (size_t t = 0; t < numTriangles; ++t)
- {
- // Get the triangle vertices.
- for (int k = 0; k < 3; ++k)
- {
- triangle.v[k] = mPoints[*currIndex++];
- }
- // Test for intersection.
- if (rtQuery(ray, triangle).intersect)
- {
- // The ray intersects the triangle.
- odd = !odd;
- }
- }
- }
- if (odd)
- {
- insideCount++;
- }
- }
- return insideCount > mNumRays / 2;
- }
- bool ContainsS1(Vector3<Real> const& p) const
- {
- int insideCount = 0;
- FIQuery<Real, Ray3<Real>, Plane3<Real>> rpQuery;
- Ray3<Real> ray;
- ray.origin = p;
- for (int j = 0; j < mNumRays; ++j)
- {
- ray.direction = mDirections[j];
- // Zero intersections to start with.
- bool odd = false;
- SimpleFace const* face = mSFaces;
- for (int i = 0; i < mNumFaces; ++i, ++face)
- {
- // Attempt to quickly cull the triangle.
- if (FastNoIntersect(ray, face->plane))
- {
- continue;
- }
- // Compute the ray-plane intersection.
- auto result = rpQuery(ray, face->plane);
- // If you trigger this assertion, numerical round-off
- // errors have led to a discrepancy between
- // FastNoIntersect and the Find() result.
- LogAssert(result.intersect, "Unexpected condition.");
- // Get a coordinate system for the plane. Use vertex 0
- // as the origin.
- Vector3<Real> const& V0 = mPoints[face->indices[0]];
- Vector3<Real> basis[3];
- basis[0] = face->plane.normal;
- ComputeOrthogonalComplement(1, basis);
- // Project the intersection onto the plane.
- Vector3<Real> diff = result.point - V0;
- Vector2<Real> projIntersect{ Dot(basis[1], diff), Dot(basis[2], diff) };
- // Project the face vertices onto the plane of the face.
- if (face->indices.size() > mProjVertices.size())
- {
- mProjVertices.resize(face->indices.size());
- }
- // Project the remaining vertices. Vertex 0 is always the
- // origin.
- size_t numIndices = face->indices.size();
- mProjVertices[0] = Vector2<Real>::Zero();
- for (size_t k = 1; k < numIndices; ++k)
- {
- diff = mPoints[face->indices[k]] - V0;
- mProjVertices[k][0] = Dot(basis[1], diff);
- mProjVertices[k][1] = Dot(basis[2], diff);
- }
- // Test whether the intersection point is in the convex
- // polygon.
- PointInPolygon2<Real> PIP(static_cast<int>(mProjVertices.size()),
- &mProjVertices[0]);
- if (PIP.Contains(projIntersect))
- {
- // The ray intersects the triangle.
- odd = !odd;
- }
- }
- if (odd)
- {
- insideCount++;
- }
- }
- return insideCount > mNumRays / 2;
- }
- int mNumPoints;
- Vector3<Real> const* mPoints;
- int mNumFaces;
- TriangleFace const* mTFaces;
- ConvexFace const* mCFaces;
- SimpleFace const* mSFaces;
- unsigned int mMethod;
- int mNumRays;
- Vector3<Real> const* mDirections;
- // Temporary storage for those methods that reduce the problem to 2D
- // point-in-polygon queries. The array stores the projections of
- // face vertices onto the plane of the face. It is resized as needed.
- mutable std::vector<Vector2<Real>> mProjVertices;
- };
- }
|