24 typedef VectorType<unsigned,
D - 1> EdgeType;
26 SmartPointer<Mesh<T>> mesh =
nullptr;
27 SmartPointer<PointCloud<T, D>> pointCloud =
nullptr;
28 std::vector<EdgeType> visitedEdges;
29 std::vector<VectorType<unsigned, D>> hullElements;
30 std::list<EdgeType> remainingEdges;
35 unsigned pivotEdge(EdgeType currentEdge)
const {
36 auto &points = pointCloud->points;
37 unsigned nextIndex = 0;
40 while (nextIndex == currentEdge[0] ||
41 (
D == 3 && nextIndex == currentEdge[1])) {
46 if constexpr (
D == 3) {
47 unsigned searchIndex = nextIndex;
48 for (; searchIndex < points.size(); ++searchIndex) {
49 if (searchIndex == currentEdge[0] || searchIndex == currentEdge[1])
52 auto v1 = points[currentEdge[1]] - points[currentEdge[0]];
53 auto v2 = points[searchIndex] - points[currentEdge[0]];
54 auto cross = CrossProduct(v1, v2);
55 if (DotProduct(cross, cross) > 1e-10) {
56 nextIndex = searchIndex;
63 std::vector<unsigned> previousCandidates;
65 for (
unsigned i = 0; i < points.size(); ++i) {
66 if (i == currentEdge[0] || i == nextIndex)
68 if (
D == 3 && i == currentEdge[1])
72 VectorType<T, D> normal;
73 VectorType<T, D> distance = points[i] - points[currentEdge[0]];
75 if constexpr (
D == 2) {
77 normal = points[nextIndex] - points[currentEdge[0]];
79 std::swap(normal[0], normal[1]);
80 normal[0] = -normal[0];
82 }
else if constexpr (
D == 3) {
83 auto v1 = points[currentEdge[1]] - points[currentEdge[0]];
84 auto v2 = points[nextIndex] - points[currentEdge[0]];
85 normal = CrossProduct(v1, v2);
89 auto product = DotProduct(distance, normal);
95 if (std::abs(product) < 1e-9) {
98 VectorType<unsigned, D> triangle;
99 triangle[0] = currentEdge[0];
100 triangle[1] = currentEdge[1];
102 if (doesTriangleClip(triangle)) {
107 edges[0][0] = currentEdge[0];
109 edges[1][0] = currentEdge[1];
112 if (!wasEdgeVisited(edges[0]) && !wasEdgeVisited(edges[1])) {
114 auto distI = DotProduct(distance, distance);
115 auto distNextVec = points[nextIndex] - points[currentEdge[0]];
116 auto distNext = DotProduct(distNextVec, distNextVec);
117 if (distI < distNext) {
123 else if (product > 0) {
129 bool cycleDetected =
false;
130 for (
unsigned prev : previousCandidates) {
132 cycleDetected =
true;
141 previousCandidates.push_back(nextIndex);
150 bool wasEdgeVisited(
const EdgeType &edge)
const {
151 for (
unsigned i = 0; i < visitedEdges.size(); ++i) {
153 if (edge[0] == visitedEdges[i][0]) {
154 if constexpr (
D == 3) {
155 if (edge[1] == visitedEdges[i][1]) {
164 if constexpr (
D == 3) {
165 if (edge[0] == visitedEdges[i][1] && edge[1] == visitedEdges[i][0]) {
174 typename std::list<EdgeType>::iterator findEdge(EdgeType edge) {
175 for (
typename std::list<EdgeType>::iterator it = remainingEdges.begin();
176 it != remainingEdges.end(); ++it) {
177 bool containsElem[2] = {
false,
false};
178 for (
unsigned i = 0; i <
D - 1; ++i) {
180 for (
unsigned k = 0; k <
D - 1; ++k) {
181 if ((*it)[i] == edge[k]) {
182 containsElem[i] =
true;
186 if (containsElem[0] && containsElem[1]) {
190 return remainingEdges.end();
194 bool intersectSharedNode(
const VectorType<unsigned, D> &triangle1,
195 const VectorType<unsigned, D> &triangle2)
const {
197 std::vector<unsigned> sharedNodes;
199 std::vector<unsigned> otherNodes;
201 for (
unsigned i = 0; i <
D; ++i) {
202 unsigned shared = sharedNodes.size();
203 for (
unsigned j = 0; j <
D; ++j) {
204 if (triangle1[i] == triangle2[j]) {
206 sharedNodes.push_back(j);
210 if (shared == sharedNodes.size()) {
211 otherNodes.push_back(i);
215 assert(!sharedNodes.empty());
217 auto &points = pointCloud->points;
225 for (
unsigned int &sharedNode : sharedNodes) {
226 VectorType<T, D> averageVector;
228 double centerEdgeDot;
230 auto shared = sharedNode;
231 VectorType<T, D> edge1 = Normalize(points[triangle2[(shared + 1) %
D]] -
232 points[triangle2[shared]]);
233 VectorType<T, D> edge2 = Normalize(points[triangle2[(shared + 2) %
D]] -
234 points[triangle2[shared]]);
235 averageVector = edge1 + edge2;
236 averageVector = averageVector /
T(2);
237 Normalize(averageVector);
238 centerEdgeDot = DotProduct(averageVector, edge2);
242 for (
unsigned int &otherNode : otherNodes) {
243 VectorType<T, D> vector = Normalize(points[triangle1[otherNode]] -
244 points[triangle2[sharedNode]]);
246 if (DotProduct(vector, averageVector) > centerEdgeDot + 1e-9) {
256 bool doesTriangleClip(
const VectorType<unsigned, D> &triangle)
const {
257 auto &points = pointCloud->points;
259 auto triangleNormal =
260 calculateNormal(points[triangle[1]] - points[triangle[0]],
261 points[triangle[2]] - points[triangle[0]]);
264 for (
unsigned i = 0; i < hullElements.size(); ++i) {
265 unsigned inOneTriangle = 0;
266 for (
unsigned j = 0; j <
D; ++j) {
268 for (
unsigned k = 0; k <
D; ++k) {
269 if (hullElements[i][j] == triangle[k]) {
276 if (inOneTriangle > 0) {
278 auto normal2 = calculateNormal(
279 points[hullElements[i][1]] - points[hullElements[i][0]],
280 points[hullElements[i][2]] - points[hullElements[i][0]]);
283 for (
unsigned d = 0; d <
D; ++d) {
285 std::abs(std::abs(triangleNormal[d]) - std::abs(normal2[d]));
293 if (intersectSharedNode(triangle, hullElements[i]))
295 if (intersectSharedNode(hullElements[i], triangle))
303 VectorType<T, D> calculateNormal(
const VectorType<T, D> &v1,
304 const VectorType<T, D> &v2)
const {
305 VectorType<T, D> newNormal;
306 newNormal[0] = v1[1] * v2[2] - v1[2] * v2[1];
307 newNormal[1] = v1[2] * v2[0] - v1[0] * v2[2];
308 newNormal[2] = v1[0] * v2[1] - v1[1] * v2[0];
324 : mesh(passedMesh), pointCloud(passedPointCloud) {}
329 pointCloud = passedPointCloud;
333 if (mesh ==
nullptr) {
334 Logger::getInstance()
335 .addError(
"No mesh was passed to ConvexHull.")
339 if (pointCloud ==
nullptr) {
340 Logger::getInstance()
341 .addError(
"No point cloud was passed to ConvexHull.")
347 auto &points = pointCloud->points;
348 if (points.size() == 0)
353 auto it = std::min_element(points.begin(), points.end());
354 unsigned currentIndex = std::distance(points.begin(), it);
357 remainingEdges.push_back(EdgeType{currentIndex});
360 int currentPointIndex = -1;
361 double hullMetric = std::numeric_limits<double>::max();
362 for (
int i = 0; i < points.size(); ++i) {
363 if (i == currentIndex)
365 if (std::abs(points[i][2] - points[currentIndex][2]) < 1e-7) {
366 auto diff = points[currentIndex] - points[i];
368 if (
const double currentHullMetric = DotProduct(diff, diff);
369 currentHullMetric < hullMetric) {
370 hullMetric = currentHullMetric;
371 currentPointIndex = i;
377 edge[0] = currentIndex;
379 if (currentPointIndex == -1) {
380 VectorType<T, D> newPoint;
381 newPoint = points[currentIndex];
384 points.push_back(newPoint);
385 edge[1] = points.size() - 1;
386 edge[1] = pivotEdge(edge);
389 edge[1] = currentPointIndex;
392 remainingEdges.push_back(edge);
397 while (!remainingEdges.empty()) {
399 EdgeType currentEdge = remainingEdges.back();
400 remainingEdges.pop_back();
402 if (wasEdgeVisited(currentEdge))
406 unsigned nextIndex = pivotEdge(currentEdge);
409 VectorType<unsigned, D> element;
410 for (
unsigned i = 0; i <
D - 1; ++i) {
411 element[i] = currentEdge[i];
413 element[
D - 1] = nextIndex;
414 hullElements.push_back(element);
417 visitedEdges.push_back(currentEdge);
419 remainingEdges.push_back(EdgeType{nextIndex});
423 edge[1] = currentEdge[1];
427 auto it = findEdge(edge);
428 if (it != remainingEdges.end()) {
429 remainingEdges.erase(it);
431 remainingEdges.push_back(edge);
434 edge[0] = currentEdge[0];
437 if (it != remainingEdges.end()) {
438 remainingEdges.erase(it);
440 remainingEdges.push_back(edge);
446 auto &elements = mesh->template getElements<D>();
447 std::unordered_map<unsigned, unsigned> oldToNewNodes;
448 for (
unsigned i = 0; i < hullElements.size(); ++i) {
450 std::array<unsigned, D> newElement;
451 for (
unsigned j = 0; j <
D; ++j) {
452 auto insertion = oldToNewNodes.insert(std::make_pair(
453 hullElements[i][j],
static_cast<unsigned>(oldToNewNodes.size())));
456 if (insertion.second) {
457 Vec3D<T> node{points[hullElements[i][j]][0],
458 points[hullElements[i][j]][1],
459 (
D == 2) ?
T(0.) : points[hullElements[i][j]][2]};
460 mesh->nodes.push_back(node);
463 newElement[j] = insertion.first->second;
466 elements.push_back(newElement);