You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

901 lines
36 KiB

// David Eberly, Geometric Tools, Redmond WA 98052
// Copyright (c) 1998-2021
// 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.6.2020.09.18
#pragma once
#include <Mathematics/FIQuery.h>
#include <Mathematics/ArbitraryPrecision.h>
#include "ConvexMesh3.h"
#include <Mathematics/EdgeKey.h>
#include <Mathematics/Hyperplane.h>
#include <Mathematics/UniqueVerticesSimplices.h>
namespace gte
{
template <typename Real>
class FIQuery<Real, ConvexMesh3<Real>, Plane3<Real>>
{
public:
// Convenient type definitions.
using CM = ConvexMesh3<Real>;
using Vertex = typename CM::Vertex;
using Triangle = typename CM::Triangle;
// The configuration describes geometrically how the input convex
// polyhedron and the plane intersect.
static int constexpr CFG_EMPTY = 0x00000000;
static int constexpr CFG_POS_SIDE = 0x00000010;
static int constexpr CFG_NEG_SIDE = 0x00000020;
// The plane intersects the convex polyhedron transversely. The set of
// intersection is a convex polygon. The convex polyhedron is split
// into two convex polyhedra, one on the positive side of the plane
// and one on the negative side of the plane, both polyhedra sharing
// the convex polygon of intersection.
static int constexpr CFG_SPLIT = CFG_POS_SIDE | CFG_NEG_SIDE; // 48
// The convex polyhedron is strictly on the positive side of the
// plane.
static int constexpr CFG_POS_SIDE_STRICT = CFG_POS_SIDE; // 16
// The convex polyhedron is on the positive side of the plane with one
// vertex in the plane.
static int constexpr CFG_POS_SIDE_VERTEX = CFG_POS_SIDE | 1; // 17
// The convex polyhedron is on the positive side of the plane with one
// edge in the plane.
static int constexpr CFG_POS_SIDE_EDGE = CFG_POS_SIDE | 2; // 18
// The convex polyhedron is on the positive side of the plane with a
// polygonal face in the plane. The face can consist of multiple
// triangles.
static int constexpr CFG_POS_SIDE_POLYGON = CFG_POS_SIDE | 4; // 20
// Flags for any of the tangential cases (vertex touching, edge
// touching, face touching).
static int constexpr CFG_POS_SIDE_TANGENT = CFG_POS_SIDE | 7; // 23
// The convex polyhedron is strictly on the negative side of the
// plane.
static int constexpr CFG_NEG_SIDE_STRICT = CFG_NEG_SIDE; // 32
// The convex polyhedron is on the negative side of the plane with one
// vertex in the plane.
static int constexpr CFG_NEG_SIDE_VERTEX = CFG_NEG_SIDE | 1; // 33
// The convex polyhedron is on the negative side of the plane with one
// edge in the plane.
static int constexpr CFG_NEG_SIDE_EDGE = CFG_NEG_SIDE | 2; // 34
// The convex polyhedron is on the negative side of the plane with a
// polygonal face in the plane. The face can consist of multiple
// triangles.
static int constexpr CFG_NEG_SIDE_POLYGON = CFG_NEG_SIDE | 4; // 36
// Flags for any of the tangential cases (vertex touching, edge
// touching, face touching).
static int constexpr CFG_NEG_SIDE_TANGENT = CFG_NEG_SIDE | 7; // 39
// Requested information for the query to compute.
static int constexpr REQ_CONFIGURATION_ONLY = 0x00000000;
static int constexpr REQ_INTR_MESH = 0x00000001;
static int constexpr REQ_INTR_POLYGON = 0x00000002;
static int constexpr REQ_INTR_BOTH = REQ_INTR_MESH | REQ_INTR_POLYGON;
static int constexpr REQ_POLYHEDRON_POS = 0x00000004;
static int constexpr REQ_POLYHEDRON_NEG = 0x00000008;
static int constexpr REQ_POLYHEDRON_BOTH = REQ_POLYHEDRON_POS | REQ_POLYHEDRON_NEG;
static int constexpr REQ_ALL = 0x0000000F;
struct Result
{
// The configuration describes geometrically how the input convex
// polyhedron and the plane intersect.
int configuration = CFG_EMPTY;
// You can specify the information you want from the query.
int requested = REQ_CONFIGURATION_ONLY;
// The intersection of the convex polyhedron and the plane is
// either empty, a single vertex, a single edge or a convex
// polygon. The intersection members have the properties:
// empty: mVertices has 0 elements, mMesh is empty
// vertex: mVertices has 1 element, mMesh is empty
// edge: mVertices has 2 elements, mMesh is empty
// polygon: mVertices has 3 or more elements, mMesh is a
// triangulation of the convex polygon
// The convex polygon vertices are indexed by 'polygon' in the
// order consistent with that of the positive polyhedron
// triangles. The indices are into intersection.vertices.
ConvexMesh3<Real> intersectionMesh;
std::vector<Vertex> intersectionPolygon;
// If the configuration is POSITIVE_* or SPLIT, this convex
// polyhedron is the portion of the input convex polyhedron on the
// positive side of the plane with possibly a vertex or edge on
// the plane.
ConvexMesh3<Real> positivePolyhedron;
// If the configuration is NEGATIVE_* or SPLIT, this convex
// polyhedron is the portion of the input convex polyhedron on the
// negative side of the plane with possibly a vertex or edge on
// the plane.
ConvexMesh3<Real> negativePolyhedron;
};
Result operator() (ConvexMesh3<Real> const& polyhedron,
Plane3<Real> const& plane, int requested)
{
static_assert(is_arbitrary_precision<Real>::value, "Real must be arbitrary precision.");
static_assert(has_division_operator<Real>::value, "Real must support division.");
Result result;
result.requested = requested;
// Storage for (Dot(N,X) - c) for each vertex X, where N is a
// plane normal (not necessarily unit length) and c is the
// corresponding plane constant.
int numPositive = 0, numNegative = 0, numZero = 0;
size_t const numVertices = polyhedron.vertices.size();
std::vector<Real> dot(numVertices);
std::vector<int> sign(numVertices);
for (size_t i = 0; i < numVertices; ++i)
{
dot[i] = Dot(plane.normal, polyhedron.vertices[i]) - plane.constant;
if (dot[i] > (Real)0)
{
sign[i] = +1;
++numPositive;
}
else if (dot[i] < (Real)0)
{
sign[i] = -1;
++numNegative;
}
else
{
sign[i] = 0;
++numZero;
}
}
if (numPositive == 0)
{
result.configuration = CFG_NEG_SIDE | (numZero < 3 ? numZero : 4);
if ((requested & REQ_POLYHEDRON_NEG) != 0)
{
result.negativePolyhedron = polyhedron;
}
if (numZero > 0 && ((requested & REQ_INTR_BOTH) != 0))
{
GetIntersection(polyhedron, numZero, sign, result);
}
}
else if (numNegative == 0)
{
result.configuration = CFG_POS_SIDE | (numZero < 3 ? numZero : 4);
if ((requested & REQ_POLYHEDRON_POS) != 0)
{
result.positivePolyhedron = polyhedron;
}
if (numZero > 0 && ((requested & REQ_INTR_BOTH) != 0))
{
GetIntersection(polyhedron, numZero, sign, result);
}
}
else
{
result.configuration = CFG_SPLIT;
if (requested != REQ_CONFIGURATION_ONLY)
{
SplitPolyhedron(polyhedron, dot, sign, result);
}
}
return result;
}
private:
static void GetIntersection(CM const& polyhedron, int numZero,
std::vector<int> const& sign, Result& result)
{
bool const wantIntrMesh = (result.requested & REQ_INTR_MESH) != 0;
bool const wantIntrPolygon = (result.requested & REQ_INTR_POLYGON) != 0;
if (numZero == 1)
{
GetIntersectionVertex(polyhedron, sign, wantIntrMesh,
wantIntrPolygon, result);
}
else if (numZero == 2)
{
GetIntersectionEdge(polyhedron, sign, wantIntrMesh,
wantIntrPolygon, result);
}
else // numZero >= 3
{
GetIntersectionPolygon(polyhedron, sign, wantIntrMesh,
wantIntrPolygon, result);
}
}
static void GetIntersectionVertex(CM const& polyhedron,
std::vector<int> const& sign, bool wantIntrMesh, bool wantIntrPolygon,
Result& result)
{
result.intersectionMesh.configuration = ConvexMesh3<Real>::CFG_POINT;
if (wantIntrMesh)
{
result.intersectionMesh.vertices.resize(1);
}
if (wantIntrPolygon)
{
result.intersectionPolygon.resize(1);
}
size_t const numVertices = polyhedron.vertices.size();
for (size_t i = 0; i < numVertices; ++i)
{
if (sign[i] == 0)
{
if (wantIntrMesh)
{
result.intersectionMesh.vertices[0] = polyhedron.vertices[i];
}
if (wantIntrPolygon)
{
result.intersectionPolygon[0] = polyhedron.vertices[i];
}
return;
}
}
}
static void GetIntersectionEdge(CM const& polyhedron,
std::vector<int> const& sign, bool wantIntrMesh, bool wantIntrPolygon,
Result& result)
{
result.intersectionMesh.configuration = ConvexMesh3<Real>::CFG_SEGMENT;
if (wantIntrMesh)
{
result.intersectionMesh.vertices.resize(2);
}
if (wantIntrPolygon)
{
result.intersectionPolygon.resize(2);
}
size_t const numVertices = polyhedron.vertices.size();
for (size_t i = 0, numFound = 0; i < numVertices; ++i)
{
if (sign[i] == 0)
{
if (wantIntrMesh)
{
result.intersectionMesh.vertices[numFound] = polyhedron.vertices[i];
}
if (wantIntrPolygon)
{
result.intersectionPolygon[numFound] = polyhedron.vertices[i];
}
if (++numFound == 2)
{
return;
}
}
}
}
static void GetIntersectionPolygon(CM const& polyhedron,
std::vector<int> const& sign, bool wantIntrMesh, bool wantIntrPolygon,
Result& result)
{
result.intersectionMesh.configuration = ConvexMesh3<Real>::CFG_POLYGON;
std::vector<Triangle> intersectionMeshTriangles;
intersectionMeshTriangles.reserve(polyhedron.triangles.size());
for (auto const& triangle : polyhedron.triangles)
{
if (sign[triangle[0]] == 0 && sign[triangle[1]] == 0 && sign[triangle[2]] == 0)
{
intersectionMeshTriangles.push_back(triangle);
}
}
std::vector<Vertex> outVertices;
std::vector<Triangle> outTriangles;
UniqueVerticesSimplices<Vertex, int, 3> uvt;
uvt.RemoveDuplicateAndUnusedVertices(polyhedron.vertices,
intersectionMeshTriangles, outVertices, outTriangles);
if (wantIntrPolygon)
{
// Get the boundary edges with ordering consistent with the
// triangle face chirality.
std::map<EdgeKey<false>, std::array<int, 2>> edgeMap;
for (auto const& triangle : outTriangles)
{
for (size_t j0 = 2, j1 = 0; j1 < 3; j0 = j1++)
{
EdgeKey<false> edge(triangle[j0], triangle[j1]);
auto iter = edgeMap.find(edge);
if (iter != edgeMap.end())
{
// The edge is now visited twice, so it cannot be a
// boundary edge.
edgeMap.erase(iter);
}
else
{
// The edge is visited the first time, so it might be
// a boundary edge.
std::array<int, 2> value = { triangle[j1], triangle[j0] };
edgeMap.insert(std::make_pair(edge, value));
}
}
}
// Construct the boundary polygon.
std::vector<int> polygonIndices(edgeMap.size(), -1);
for (auto const& element : edgeMap)
{
polygonIndices[element.second[0]] = element.second[1];
}
result.intersectionPolygon.resize(edgeMap.size());
for (size_t i = 0; i < result.intersectionPolygon.size(); ++i)
{
result.intersectionPolygon[i] = outVertices[polygonIndices[i]];
}
}
if (wantIntrMesh)
{
result.intersectionMesh.vertices = std::move(outVertices);
result.intersectionMesh.triangles = std::move(outTriangles);
}
}
static void SplitPolyhedron(CM const& polyhedron, std::vector<Real> const& dot,
std::vector<int> const& sign, Result& result)
{
bool const wantPosMesh = (result.requested & REQ_POLYHEDRON_POS) != 0;
bool const wantNegMesh = (result.requested & REQ_POLYHEDRON_NEG) != 0;
bool const wantIntrMesh = (result.requested & REQ_INTR_MESH) != 0;
bool const wantIntrPolygon = (result.requested & REQ_INTR_POLYGON) != 0;
// The split polyhedra use the input polyhedron's vertices and any
// edge-interior intersections between the plane and the mesh
// edges. The center point of the polygon of intersection (if any)
// is also used as a vertex.
std::vector<Vertex> splitVertices;
std::map<EdgeKey<false>, int> eiVMap;
GetVertexCandidates(polyhedron, dot, sign, splitVertices, eiVMap);
// Split each triangle face of the polyhedron by the plane.
std::vector<Triangle> posMesh, negMesh;
std::map<int, int> posIntersection;
DoSplit(polyhedron, sign, eiVMap, wantPosMesh, posMesh,
wantNegMesh, negMesh, posIntersection);
// Get the polygon of intersection. This is used by all of the
// requested features.
std::vector<int> polygon;
GetIntersectionPolygon(posIntersection, splitVertices,
wantIntrPolygon, polygon, result);
if (wantPosMesh || wantNegMesh || wantIntrMesh)
{
// Get the polyhedra split by the plane. The polygon of
// intersection is also computed and used to close the
// polyhedra.
GetSplitPolyhedra(splitVertices, polygon, wantIntrMesh,
wantPosMesh, posMesh, wantNegMesh, negMesh, result);
}
}
static void GetVertexCandidates(CM const& polyhedron,
std::vector<Real> const& dot, std::vector<int> const& sign,
std::vector<Vertex>& splitVertices,
std::map<EdgeKey<false>, int>& eiVMap)
{
// Get the edges of the polyhedron.
std::set<EdgeKey<false>> edgeMap;
for (auto const& triangle : polyhedron.triangles)
{
edgeMap.insert(EdgeKey<false>(triangle[0], triangle[1]));
edgeMap.insert(EdgeKey<false>(triangle[1], triangle[2]));
edgeMap.insert(EdgeKey<false>(triangle[2], triangle[0]));
}
// The vertex candidates include the original vertices, any
// edge-interior intersections between the plane and polyhedron,
// and the average of the convex-polygon intersection (if there
// is such an intersection). The number of reserved elements of
// splitVertices is large enough to avoid resizing of the array
// later.
splitVertices.reserve(polyhedron.vertices.size() + edgeMap.size() + 1);
for (auto const& vertex : polyhedron.vertices)
{
splitVertices.push_back(vertex);
}
// Compute edge-interior points of intersection between the plane
// and the mesh edges. The eiVMap container allows accessing the
// edge-interior vertices when each triangle face of the
// polyhedron is processed for intersection with the plane.
for (auto const& element : edgeMap)
{
int v0 = element.V[0];
int v1 = element.V[1];
if (sign[v0] * sign[v1] < 0)
{
Real denom = dot[v1] - dot[v0];
Real w0 = dot[v1] / denom;
Real w1 = -dot[v0] / denom;
auto eiVertex =
w0 * polyhedron.vertices[v0] + w1 * polyhedron.vertices[v1];
int const eiIndex = static_cast<int>(splitVertices.size());
eiVMap.insert(std::make_pair(EdgeKey<false>(v0, v1), eiIndex));
splitVertices.push_back(eiVertex);
}
}
// The average point will be appended to splitVertices later when
// necessary.
}
static void DoSplit(CM const& polyhedron, std::vector<int> const& sign,
std::map<EdgeKey<false>, int>& eiVMap,
bool wantPosMesh, std::vector<Triangle>& posMesh,
bool wantNegMesh, std::vector<Triangle>& negMesh,
std::map<int, int>& posIntersection)
{
for (auto const& triangle : polyhedron.triangles)
{
int v0 = triangle[0], v1 = triangle[1], v2 = triangle[2];
int v01 = -1, v12 = -1, v20 = -1;
if (sign[v0] > 0)
{
if (sign[v1] > 0)
{
if (sign[v2] > 0)
{
// +++
if (wantPosMesh)
{
posMesh.push_back({ v0, v1, v2 });
}
}
else if (sign[v2] < 0)
{
// ++-
v12 = eiVMap[EdgeKey<false>(v1, v2)];
v20 = eiVMap[EdgeKey<false>(v2, v0)];
if (wantPosMesh)
{
posMesh.push_back({ v0, v12, v20 });
posMesh.push_back({ v0, v1, v12 });
}
if (wantNegMesh)
{
negMesh.push_back({ v2, v20, v12 });
}
posIntersection.insert(std::make_pair(v20, v12));
}
else
{
// ++0
if (wantPosMesh)
{
posMesh.push_back({ v0, v1, v2 });
}
}
}
else if (sign[v1] < 0)
{
if (sign[v2] > 0)
{
// +-+
v01 = eiVMap[EdgeKey<false>(v0, v1)];
v12 = eiVMap[EdgeKey<false>(v1, v2)];
if (wantPosMesh)
{
posMesh.push_back({ v0, v01, v12 });
posMesh.push_back({ v0, v12, v2 });
}
if (wantNegMesh)
{
negMesh.push_back({ v1, v12, v01 });
}
posIntersection.insert(std::make_pair(v12, v01));
}
else if (sign[v2] < 0)
{
// +--
v01 = eiVMap[EdgeKey<false>(v0, v1)];
v20 = eiVMap[EdgeKey<false>(v2, v0)];
if (wantPosMesh)
{
posMesh.push_back({ v0, v01, v20 });
}
if (wantNegMesh)
{
negMesh.push_back({ v1, v20, v01 });
negMesh.push_back({ v1, v2, v20 });
}
posIntersection.insert(std::make_pair(v20, v01));
}
else
{
// +-0
v01 = eiVMap[EdgeKey<false>(v0, v1)];
if (wantPosMesh)
{
posMesh.push_back({ v2, v0, v01 });
}
if (wantNegMesh)
{
negMesh.push_back({ v2, v01, v1 });
}
posIntersection.insert(std::make_pair(v2, v01));
}
}
else
{
if (sign[v2] > 0)
{
// +0+
if (wantPosMesh)
{
posMesh.push_back({ v0, v1, v2 });
}
}
else if (sign[v2] < 0)
{
// +0-
v20 = eiVMap[EdgeKey<false>(v2, v0)];
if (wantPosMesh)
{
posMesh.push_back({ v1, v20, v0 });
}
if (wantNegMesh)
{
negMesh.push_back({ v1, v2, v20 });
}
posIntersection.insert(std::make_pair(v20, v1));
}
else
{
// +00
if (wantPosMesh)
{
posMesh.push_back({ v0, v1, v2 });
}
posIntersection.insert(std::make_pair(v2, v1));
}
}
}
else if (sign[v0] < 0)
{
if (sign[v1] > 0)
{
if (sign[v2] > 0)
{
// -++
v01 = eiVMap[EdgeKey<false>(v0, v1)];
v20 = eiVMap[EdgeKey<false>(v2, v0)];
if (wantPosMesh)
{
posMesh.push_back({ v1, v20, v01 });
posMesh.push_back({ v1, v2, v20 });
}
if (wantNegMesh)
{
negMesh.push_back({ v0, v01, v20 });
}
posIntersection.insert(std::make_pair(v01, v20));
}
else if (sign[v2] < 0)
{
// -+-
v01 = eiVMap[EdgeKey<false>(v0, v1)];
v12 = eiVMap[EdgeKey<false>(v1, v2)];
if (wantPosMesh)
{
posMesh.push_back({ v1, v12, v01 });
}
if (wantNegMesh)
{
negMesh.push_back({ v0, v01, v12 });
negMesh.push_back({ v0, v12, v2 });
}
posIntersection.insert(std::make_pair(v01, v12));
}
else
{
// -+0
v01 = eiVMap[EdgeKey<false>(v0, v1)];
if (wantPosMesh)
{
posMesh.push_back({ v1, v2, v01 });
}
if (wantNegMesh)
{
negMesh.push_back({ v2, v0, v01 });
}
posIntersection.insert(std::make_pair(v01, v2));
}
}
else if (sign[v1] < 0)
{
if (sign[v2] > 0)
{
// --+
v12 = eiVMap[EdgeKey<false>(v1, v2)];
v20 = eiVMap[EdgeKey<false>(v2, v0)];
if (wantPosMesh)
{
posMesh.push_back({ v2, v20, v12 });
}
if (wantNegMesh)
{
negMesh.push_back({ v0, v1, v12 });
negMesh.push_back({ v0, v12, v20 });
}
posIntersection.insert(std::make_pair(v12, v20));
}
else if (sign[v2] < 0)
{
// ---
if (wantNegMesh)
{
negMesh.push_back({ v0, v1, v2 });
}
}
else
{
// --0
if (wantNegMesh)
{
negMesh.push_back({ v0, v1, v2 });
}
}
}
else
{
if (sign[v2] > 0)
{
// -0+
v20 = eiVMap[EdgeKey<false>(v2, v0)];
if (wantPosMesh)
{
posMesh.push_back({ v2, v20, v1 });
}
if (wantNegMesh)
{
negMesh.push_back({ v0, v1, v20 });
}
posIntersection.insert(std::make_pair(v1, v20));
}
else if (sign[v2] < 0)
{
// -0-
if (wantNegMesh)
{
negMesh.push_back({ v0, v1, v2 });
}
}
else
{
// -00
if (wantNegMesh)
{
negMesh.push_back({ v0, v1, v2 });
}
}
}
}
else
{
if (sign[v1] > 0)
{
if (sign[v2] > 0)
{
// 0++
if (wantPosMesh)
{
posMesh.push_back({ v0, v1, v2 });
}
}
else if (sign[v2] < 0)
{
// 0+-
v12 = eiVMap[EdgeKey<false>(v1, v2)];
if (wantPosMesh)
{
posMesh.push_back({ v1, v12, v0 });
}
if (wantNegMesh)
{
negMesh.push_back({ v2, v0, v12 });
}
posIntersection.insert(std::make_pair(v0, v12));
}
else
{
// 0+0
if (wantPosMesh)
{
posMesh.push_back({ v0, v1, v2 });
}
posIntersection.insert(std::make_pair(v0, v2));
}
}
else if (sign[v1] < 0)
{
if (sign[v2] > 0)
{
// 0-+
v12 = eiVMap[EdgeKey<false>(v1, v2)];
if (wantPosMesh)
{
posMesh.push_back({ v2, v0, v12 });
}
if (wantNegMesh)
{
negMesh.push_back({ v1, v12, v0 });
}
posIntersection.insert(std::make_pair(v12, v0));
}
else if (sign[v2] < 0)
{
// 0--
if (wantNegMesh)
{
negMesh.push_back({ v0, v1, v2 });
}
}
else
{
// 0-0
if (wantNegMesh)
{
negMesh.push_back({ v0, v1, v2 });
}
}
}
else
{
if (sign[v2] > 0)
{
// 00+
if (wantPosMesh)
{
posMesh.push_back({ v0, v1, v2 });
}
posIntersection.insert(std::make_pair(v1, v0));
}
else if (sign[v2] < 0)
{
// 00-
if (wantNegMesh)
{
negMesh.push_back({ v0, v1, v2 });
}
}
else
{
// 000
// This case cannot occur with exact arithmetic,
// because it would have been trapped previously
// by tests numPositive == 0 or numNegative == 0.
LogError("This case cannot occur with exact arithmetic.");
}
}
}
}
}
static void GetIntersectionPolygon(std::map<int, int> const& posIntersection,
std::vector<Vertex>& splitVertices, bool wantIntrPolygon,
std::vector<int>& polygon, Result& result)
{
size_t const numVertices = posIntersection.size();
polygon.resize(numVertices);
auto posIter = posIntersection.begin();
for (size_t i = 0; i < numVertices; ++i)
{
polygon[i] = posIter->first;
posIter = posIntersection.find(posIter->second);
}
if (wantIntrPolygon)
{
result.intersectionPolygon.resize(numVertices);
for (size_t i = 0; i < numVertices; ++i)
{
result.intersectionPolygon[i] = splitVertices[polygon[i]];
}
}
}
static void GetSplitPolyhedra(std::vector<Vertex>& splitVertices,
std::vector<int> const& polygon, bool wantIntrMesh,
bool wantPosMesh, std::vector<Triangle>& posMesh,
bool wantNegMesh, std::vector<Triangle>& negMesh, Result& result)
{
// Triangulate the polygon for use by the positive polyhedron. A
// triangle fan will not work always work when the polygon has
// collinear vertices. The average of the polygon vertices is
// inserted as an extra vertex. The triangulation includes each
// triangle that is formed by the average point and an edge of the
// polygon. The negative polyhedron uses the same triangulation
// but with opposite chirality. NOTE: To avoid biases in the
// average due to vertex distribution, use the center of mass of
// the polygon instead.
Vertex average{ (Real)0, (Real)0, (Real)0 };
for (auto const& i : polygon)
{
average += splitVertices[i];
}
int const numVertices = static_cast<int>(polygon.size());
average /= static_cast<Real>(numVertices);
int iAvrIndex = static_cast<int>(splitVertices.size());
splitVertices.push_back(average);
std::vector<Triangle> intrMesh;
for (int i0 = numVertices - 1, i1 = 0; i1 < numVertices; i0 = i1++)
{
if (wantPosMesh)
{
posMesh.push_back({ iAvrIndex, polygon[i0], polygon[i1] });
}
if (wantNegMesh)
{
negMesh.push_back({ iAvrIndex, polygon[i1], polygon[i0] });
}
if (wantIntrMesh)
{
intrMesh.push_back({ iAvrIndex, polygon[i0], polygon[i1] });
}
}
UniqueVerticesSimplices<Vertex, int, 3> uvt;
if (wantPosMesh)
{
result.positivePolyhedron.configuration = CM::CFG_POLYHEDRON;
uvt.RemoveDuplicateAndUnusedVertices(splitVertices, posMesh,
result.positivePolyhedron.vertices,
result.positivePolyhedron.triangles);
}
if (wantNegMesh)
{
result.negativePolyhedron.configuration = CM::CFG_POLYHEDRON;
uvt.RemoveDuplicateAndUnusedVertices(splitVertices, negMesh,
result.negativePolyhedron.vertices,
result.negativePolyhedron.triangles);
}
if (wantIntrMesh)
{
result.intersectionMesh.configuration = CM::CFG_POLYGON;
uvt.RemoveDuplicateAndUnusedVertices(splitVertices, intrMesh,
result.intersectionMesh.vertices,
result.intersectionMesh.triangles);
}
}
};
}