convexHull.hpp
Go to the documentation of this file.
1/*
2 * This file is part of ArmarX.
3 *
4 * Copyright (C) 2011-2016, High Performance Humanoid Technologies (H2T), Karlsruhe Institute of Technology (KIT), all rights reserved.
5 *
6 * ArmarX is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10 * ArmarX is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * @package
19 * @author
20 * @date
21 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
22 * GNU General Public License
23 */
24#pragma once
25
26#include <cassert>
27#include <iostream>
28#include <memory>
29#include <vector>
30
31#include "point.hpp"
32
33template <class T>
35{
36public:
37 struct Face;
38 struct HalfEdge;
39 using FacePtr = std::shared_ptr<Face>;
40 using HalfEdgePtr = std::shared_ptr<HalfEdge>;
41
50
51 struct Face
52 {
53 Face() : outerComponent(NULL), lastVisitedBy(NULL), toDelete(false)
54 {
55 }
56
57 HalfEdgePtr outerComponent; //The face lies on the left of the edge
58 std::vector<HalfEdgePtr> innerComponents;
59 std::vector<const T*> visiblePoints;
62 };
63
64 // ~DoublyLinkedEdgeList() {
65 // for(HalfEdgePtr e : mHalfEdges) {
66 // delete e;
67 // }
68 // for(FacePtr f : mFaces) {
69 // delete f;
70 // }
71 // }
72
73 std::vector<FacePtr>
75 {
76 return mFaces;
77 }
78
79 /*
80 * Check if the face f is visible from point p
81 */
82 static bool
83 visible(const FacePtr& f, const T& p)
84 {
85 HalfEdgePtr a = f->outerComponent;
86 HalfEdgePtr b = a->next;
87 HalfEdgePtr c = b->next;
88
89 T x = sub(*b->origin, *a->origin);
90 T y = sub(*c->origin, *a->origin);
91 T n = cross(x, y);
92
93 //The triangle is only visible if the product of the
94 //normal and P - A is positive, i.e. smaller than 90 degs.
95 return dot(n, sub(p, *(a->origin))) >= 0;
96 }
97
98 /*
99 * (Positive) distance from a point to a face
100 */
101 static double
102 distance(const FacePtr& f, const T& p)
103 {
104 auto e = f->outerComponent;
105 const T* a = e->origin;
106 const T* b = e->next->origin;
107 const T* c = e->next->next->origin;
108
109 return distanceToTriangle(*a, *b, *c, p);
110 }
111
112 /*
113 * Add a CCW triangular face, where e is the base and p the
114 * opposite point.
115 */
116 FacePtr
117 addFace(HalfEdgePtr e, const T* p)
118 {
119 FacePtr f(new Face());
120 f->outerComponent = e;
121 HalfEdgePtr ep(new HalfEdge());
122 HalfEdgePtr pe(new HalfEdge());
123 ep->origin = e->twin->origin;
124 pe->origin = p;
125 ep->incidentFace = pe->incidentFace = e->incidentFace = f;
126 e->next = ep;
127 ep->next = pe;
128 pe->next = e;
129 e->prev = pe;
130 pe->prev = ep;
131 ep->prev = e;
132
133 //Note that here we don't use the `twin->origin` tests
134 assert(e->prev->next == e);
135 assert(e->next->prev == e);
136 assert(e->twin->twin == e);
137 assert(e->prev->incidentFace == e->incidentFace);
138 assert(e->next->incidentFace == e->incidentFace);
139
140 assert(ep->prev->next == ep);
141 assert(ep->next->prev == ep);
142 assert(ep->prev->incidentFace == ep->incidentFace);
143 assert(ep->next->incidentFace == ep->incidentFace);
144
145 assert(pe->prev->next == pe);
146 assert(pe->next->prev == pe);
147 assert(pe->prev->incidentFace == pe->incidentFace);
148 assert(pe->next->incidentFace == pe->incidentFace);
149
150 assert(f->outerComponent->incidentFace == f);
151 mFaces.push_back(f);
152 return f;
153 }
154
155 double
157 {
158 return m_area;
159 }
160
161 void
163 {
164 double area = 0.0;
165
166 //Heron's formula
167 for (const auto& f : mFaces)
168 {
169 HalfEdgePtr e = f->outerComponent;
170 const T* x = e->origin;
171 const T* y = e->next->origin;
172 const T* z = e->next->next->origin;
173
174 double a = norm(sub(*y, *x));
175 double b = norm(sub(*z, *x));
176 double c = norm(sub(*y, *z));
177 double p = (a + b + c) / 2;
178
179 area += sqrt(abs(p * (p - a) * (p - b) * (p - c)));
180 }
181
182 m_area = area;
183 }
184
185 double
187 {
188 return m_volume;
189 }
190
191 void
193 {
194 double volume = 0.0;
195 //Use the origin as the center of the volume calculation
196 //TODO make this more robust (centroid?)
197 T center(0, 0, 0);
198
199 for (const auto& f : mFaces)
200 {
201 HalfEdgePtr e = f->outerComponent;
202 const T* x = e->origin;
203 const T* y = e->next->origin;
204 const T* z = e->next->next->origin;
205
206 double a = norm(sub(*y, *x));
207 double b = norm(sub(*z, *x));
208 double c = norm(sub(*y, *z));
209 double p = (a + b + c) / 2;
210
211 double area = sqrt(abs(p * (p - a) * (p - b) * (p - c)));
212 double height = distance(f, center);
213 volume += area * height / 3.0;
214 }
215
216 m_volume = volume;
217 }
218
219 /*
220 * Assume the first three vertices are already in CCW fashion
221 * when looking at the base from the bottom, i.e. the border
222 * of the base is a->b->c->a.
223 */
224 void
225 addTetrahedron(const T& a, const T& b, const T& c, const T& d)
226 {
227 //This code is going to be sooo buggy.....
228
229 //Create the base
230 FacePtr base(new Face());
231 HalfEdgePtr ab(new HalfEdge()); //Base edge from a and twin
232 HalfEdgePtr ba(new HalfEdge());
233 ab->twin = ba;
234 ba->twin = ab;
235 ab->origin = &a;
236 ba->origin = &b;
237 ab->incidentFace = base;
238 mHalfEdges.push_back(ab);
239 mHalfEdges.push_back(ba);
240
241 HalfEdgePtr bc(new HalfEdge()); //Base edge from b and twin
242 HalfEdgePtr cb(new HalfEdge());
243 bc->twin = cb;
244 cb->twin = bc;
245 bc->origin = &b;
246 cb->origin = &c;
247 bc->incidentFace = base;
248 mHalfEdges.push_back(bc);
249 mHalfEdges.push_back(cb);
250
251 HalfEdgePtr ca(new HalfEdge()); //Base edge from c and twin
252 HalfEdgePtr ac(new HalfEdge());
253 ca->twin = ac;
254 ac->twin = ca;
255 ca->origin = &c;
256 ac->origin = &a;
257 ca->incidentFace = base;
258 mHalfEdges.push_back(ca);
259 mHalfEdges.push_back(ac);
260
261 base->outerComponent = ab;
262
263 //Link the edges
264 ab->next = bc;
265 bc->next = ca;
266 ca->next = ab;
267 ab->prev = ca;
268 bc->prev = ab;
269 ca->prev = bc;
270
271 mFaces.push_back(base);
272
273 assert(!DoublyLinkedEdgeList<T>::visible(base, d));
274
275 //Create the edges from the point
276 HalfEdgePtr bd(new HalfEdge());
277 HalfEdgePtr db(new HalfEdge());
278 bd->twin = db;
279 db->twin = bd;
280 bd->origin = &b;
281 db->origin = &d;
282 mHalfEdges.push_back(bd);
283 mHalfEdges.push_back(db);
284
285 HalfEdgePtr da(new HalfEdge());
286 HalfEdgePtr ad(new HalfEdge());
287 da->twin = ad;
288 ad->twin = da;
289 da->origin = &d;
290 ad->origin = &a;
291 mHalfEdges.push_back(da);
292 mHalfEdges.push_back(ad);
293
294 HalfEdgePtr dc(new HalfEdge());
295 HalfEdgePtr cd(new HalfEdge());
296 dc->twin = cd;
297 cd->twin = dc;
298 dc->origin = &d;
299 cd->origin = &c;
300 mHalfEdges.push_back(dc);
301 mHalfEdges.push_back(cd);
302
303 //Now the other faces
304 FacePtr bad(new Face());
305 ba->incidentFace = db->incidentFace = ad->incidentFace = bad;
306 ba->next = ad;
307 ad->next = db;
308 db->next = ba;
309 ba->prev = db;
310 db->prev = ad;
311 ad->prev = ba;
312 bad->outerComponent = ba;
313 mFaces.push_back(bad);
314
315 FacePtr acd(new Face());
316 ac->incidentFace = cd->incidentFace = da->incidentFace = acd;
317 ac->next = cd;
318 cd->next = da;
319 da->next = ac;
320 ac->prev = da;
321 da->prev = cd;
322 cd->prev = ac;
323 acd->outerComponent = ac;
324 mFaces.push_back(acd);
325
326 FacePtr cbd(new Face());
327 cb->incidentFace = bd->incidentFace = dc->incidentFace = cbd;
328 cb->next = bd;
329 bd->next = dc;
330 dc->next = cb;
331 cb->prev = dc;
332 dc->prev = bd;
333 bd->prev = cb;
334 cbd->outerComponent = cb;
335 mFaces.push_back(cbd);
336
337 //Now try to make sense of this mess
338 for (FacePtr f : mFaces)
339 {
340 assert(f->outerComponent->incidentFace == f);
341 }
342
343 for (HalfEdgePtr e : mHalfEdges)
344 {
345 assert(e->prev->next == e);
346 assert(e->next->prev == e);
347 assert(e->twin->twin == e);
348 assert(e->twin->next->origin == e->origin);
349 assert(e->prev->twin->origin == e->origin);
350 assert(e->prev->incidentFace == e->incidentFace);
351 assert(e->next->incidentFace == e->incidentFace);
352 }
353 }
354
355 void
357 {
358 mFaces.erase(remove_if(mFaces.begin(), mFaces.end(), [](FacePtr f) { return f->toDelete; }),
359 mFaces.end());
360 }
361
362private:
363 std::vector<HalfEdgePtr> mHalfEdges;
364 std::vector<FacePtr> mFaces;
365
366 double m_volume;
367 double m_area;
368};
369
370/*
371 * Weird pre-C++11 deficiency,
372 * you have to use ::type because we cannot use
373 * `using ConvexHull = DoublyLinkedEdgeList<T>`
374 */
375template <typename T>
377{
379};
380
381namespace Convex
382{
383 template <class Point>
384 std::vector<const Point*>
385 extremePoints(const std::vector<Point>& points)
386 {
387 std::vector<const Point*> tetraPoints;
388 const Point *a, *b, *c, *d;
389 double dmax, dcur;
390 dmax = dcur = 0.0;
391 const Point* tmp[6] = {&points[0],
392 &points[0], //x min, max
393 &points[0],
394 &points[0], //y min, max
395 &points[0],
396 &points[0]}; //z min, max
397
398 for (unsigned int p = 0; p < points.size(); p++)
399 {
400 if (points.at(p)[0] < (*tmp[0])[0])
401 {
402 tmp[0] = &points.at(p);
403 }
404
405 if (points.at(p)[0] > (*tmp[1])[0])
406 {
407 tmp[1] = &points.at(p);
408 }
409
410 if (points.at(p)[1] < (*tmp[2])[1])
411 {
412 tmp[2] = &points.at(p);
413 }
414
415 if (points.at(p)[1] > (*tmp[3])[1])
416 {
417 tmp[3] = &points.at(p);
418 }
419
420 if (points.at(p)[2] < (*tmp[4])[2])
421 {
422 tmp[4] = &points.at(p);
423 }
424
425 if (points.at(p)[2] > (*tmp[5])[2])
426 {
427 tmp[5] = &points.at(p);
428 }
429 }
430
431 //Find the two most distant points
432 for (int i = 0; i < 6; i++)
433 {
434 for (int j = i + 1; j < 6; j++)
435 {
436 dcur = distance(*tmp[i], *tmp[j]);
437
438 if (dmax < dcur)
439 {
440 dmax = dcur;
441 a = tmp[i];
442 b = tmp[j];
443 }
444 }
445 }
446
447 //Find the most distant point to the line
448 dmax = 0.0;
449
450 for (int i = 0; i < 6; i++)
451 {
452 dcur = distanceToLine(*a, *b, *tmp[i]);
453
454 if (dmax < dcur)
455 {
456 dmax = dcur;
457 c = tmp[i];
458 }
459 }
460
461 //Find the most distant point to the plane (from the whole point list)
462 dmax = 0.0;
463
464 for (unsigned int i = 0; i < points.size(); i++)
465 {
466 dcur = distanceToTriangle(*a, *b, *c, points.at(i));
467
468 if (dmax < dcur)
469 {
470 dmax = dcur;
471 d = &points.at(i);
472 }
473 }
474
475 if (inFront(*a, *b, *c, *d))
476 {
477 tetraPoints.push_back(b);
478 tetraPoints.push_back(a);
479 }
480 else
481 {
482 tetraPoints.push_back(a);
483 tetraPoints.push_back(b);
484 }
485
486 tetraPoints.push_back(c);
487 tetraPoints.push_back(d);
488
489 return tetraPoints;
490 }
491
492 /*
493 * Implementation based on the QuickHull algorithm. The idea is to assign to each face of
494 * the CH the points from which it is visible. If this list is non-empty, this face should
495 * not be on the CH, and has to be processed. The faces are put on a stack. For each face on
496 * the stack, a cone is built from the furthest point and its horizon edges. The points
497 * visible from the old faces are reassigned to the new faces.
498 */
499 template <class Point>
501 convexHull(const std::vector<Point>& points)
502 {
503 std::cout << "CH -----------------------------------------------------------------"
504 << std::endl;
505 const double eps = 0.001;
506
507 //Try to find four non-coplanar points
508 auto tetraPoints = extremePoints<Point>(points);
509
510 assert(tetraPoints.size() == 4);
511
512 typename ConvexHull<Point>::type CH;
513
514 CH.addTetrahedron(*tetraPoints[0], *tetraPoints[1], *tetraPoints[2], *tetraPoints[3]);
515
516 auto facesStack = CH.faces();
517 assert(facesStack.size() == 4);
518
519 //Assign points to the face that is closest
520 for (unsigned int p = 0; p < points.size(); p++)
521 {
522 bool visible = false;
523 double d = std::numeric_limits<double>::max();
524 typename ConvexHull<Point>::type::FacePtr closestFace = NULL;
525
526 //Find closest face
527 for (const auto& f : facesStack)
528 {
529 double distance = ConvexHull<Point>::type::distance(f, points.at(p));
530
531 if (ConvexHull<Point>::type::visible(f, points.at(p)) && distance > eps)
532 {
533 if (distance < d)
534 {
535 d = distance;
536 visible = true;
537 closestFace = f;
538 }
539 }
540 }
541
542 if (visible)
543 {
544 closestFace->visiblePoints.push_back(&(points.at(p)));
545 }
546 }
547
548 //Process the stack of facets
549 while (!facesStack.empty())
550 {
551 auto currentFace = facesStack.back();
552 facesStack.pop_back();
553
554 if (currentFace->visiblePoints.size() == 0)
555 {
556 continue;
557 }
558
559 //std::cout << *currentFace->outerComponent->origin << "\n" << std::endl;
560 //std::cout << *currentFace->outerComponent->next->origin << "\n" << std::endl;
561 //std::cout << *currentFace->outerComponent->next->next->origin << "\n" << std::endl;
562
563 //Find point farthest away
564 const Point* point = NULL;
565 double d = std::numeric_limits<double>::min();
566
567 for (const Point* p : currentFace->visiblePoints)
568 {
569 double distance = ConvexHull<Point>::type::distance(currentFace, *p);
570
571 if (distance >= d)
572 {
573 d = distance;
574 point = p;
575 }
576 }
577
578 //Find the horizon as seen from that point
579 typename ConvexHull<Point>::type::HalfEdgePtr horizonStart = NULL;
580 //First find all visible faces
581 std::vector<typename ConvexHull<Point>::type::FacePtr> visibleFaces;
582 std::vector<typename ConvexHull<Point>::type::FacePtr> toVisit;
583 currentFace->lastVisitedBy = point;
584 visibleFaces.push_back(currentFace);
585 toVisit.push_back(currentFace);
586
587 //Spread from the current face to the adjacent ones until no new
588 //face can be added
589 while (!toVisit.empty())
590 {
591 currentFace = toVisit.back();
592 toVisit.pop_back();
593 auto e = currentFace->outerComponent;
594
595 //Go through the adjacent faces
596 do
597 {
598 auto adjFace = e->twin->incidentFace;
599
600 if (adjFace->lastVisitedBy != point &&
601 ConvexHull<Point>::type::visible(adjFace, *point))
602 {
603 adjFace->lastVisitedBy = point;
604 visibleFaces.push_back(adjFace);
605 toVisit.push_back(adjFace);
606 }
607
608 //If the adjacent face is not visible, this edge lies on the horizon
609 //TODO pull into the othe if-branch
610 if (horizonStart == NULL && !ConvexHull<Point>::type::visible(adjFace, *point))
611 {
612 horizonStart = e;
613 }
614
615 e = e->next;
616 } while (e != currentFace->outerComponent);
617 }
618
619 assert(horizonStart != NULL);
620
621 //Mark visible faces for deletion later on
622 for (auto v : visibleFaces)
623 {
624 v->toDelete = true;
625 }
626
627 //The horizon should be convex when 2D-projected from the point
628 std::vector<typename ConvexHull<Point>::type::HalfEdgePtr> horizon;
629 auto currentHorizon = horizonStart;
630
631 //Build the horizon step by step until the loop is closed
632 do
633 {
634 horizon.push_back(currentHorizon);
635 //Find adjacent edge that is on the horizon
636 auto nextEdge = currentHorizon->next;
637
638 while (ConvexHull<Point>::type::visible(nextEdge->twin->incidentFace, *point))
639 {
640 nextEdge = nextEdge->twin->next;
641 }
642
643 currentHorizon = nextEdge;
644 } while (currentHorizon != horizonStart);
645
646
647 //Now iterate over the horizon and build the new faces
648 //Save the last one so that we can go around the horizon
649 std::vector<typename ConvexHull<Point>::type::FacePtr> newFaces;
650 auto prev = horizon.back();
651 newFaces.push_back(CH.addFace(prev, point));
652
653 for (auto e : horizon)
654 {
655 if (e != horizon.back())
656 {
657 //For each one create the new triangular facet to the point
658 auto f = CH.addFace(e, point);
659 newFaces.push_back(f);
660
661 //Assume you are going in CCW order?
662 assert(prev->twin->origin == e->origin);
663
664 //Link to the prev face
665 prev->next->twin = e->prev;
666 e->prev->twin = prev->next;
667
668 prev = e;
669 assert(e->prev->twin->twin == e->prev);
670 assert(e->prev->twin->origin == e->origin);
671 }
672 else
673 {
674 //Went through the whole horizon, join the start and the end,
675 //but don't create a new face
676 prev->next->twin = e->prev;
677 e->prev->twin = prev->next;
678
679 assert(e->prev->twin->twin == e->prev);
680 assert(e->prev->twin->origin == e->origin);
681 }
682 }
683
684 int visiblePoints = 0;
685 int assignedPoints = 0;
686
687 //Also reassign the points of the old visible faces to the new faces
688 for (auto& v : visibleFaces)
689 {
690 for (auto& p : v->visiblePoints)
691 {
692 visiblePoints++;
693 bool visible = false;
694 double d = std::numeric_limits<double>::max();
695 typename ConvexHull<Point>::type::FacePtr closestFace = NULL;
696
697 //Find closest face
698 for (auto& f : newFaces)
699 {
701
702 if (ConvexHull<Point>::type::visible(f, *p) && distance > eps)
703 {
704 if (distance < d)
705 {
706 d = distance;
707 visible = true;
708 closestFace = f;
709 }
710 }
711 }
712
713 if (visible)
714 {
715 closestFace->visiblePoints.push_back(p);
716 assignedPoints++;
717 }
718 }
719
720 v->visiblePoints.clear();
721 }
722
723 //Push the new faces into the faces stack
724 for (auto& f : newFaces)
725 {
726 if (!f->visiblePoints.empty())
727 {
728 facesStack.push_back(f);
729 }
730 }
731
732 //Remember to delete the old visible faces (which are no longer in the CH)
733 }
734
735 CH.cleanup();
736
737 CH.calculateArea();
738 CH.calculateVolume();
739
740 return CH;
741 }
742} // namespace Convex
constexpr T c
std::shared_ptr< HalfEdge > HalfEdgePtr
std::vector< FacePtr > faces()
static double distance(const FacePtr &f, const T &p)
static bool visible(const FacePtr &f, const T &p)
void addTetrahedron(const T &a, const T &b, const T &c, const T &d)
FacePtr addFace(HalfEdgePtr e, const T *p)
std::shared_ptr< Face > FacePtr
ConvexHull< Point >::type convexHull(const std::vector< Point > &points)
std::vector< const Point * > extremePoints(const std::vector< Point > &points)
This file offers overloads of toIce() and fromIce() functions for STL container types.
bool inFront(const Point &p1, const Point &p2, const Point &p3, const Point &p4)
Definition point.hpp:84
Point sub(const Point &x, const Point &y)
Definition point.hpp:46
double norm(const Point &a)
Definition point.hpp:102
double distance(const Point &a, const Point &b)
Definition point.hpp:95
double distanceToLine(const Point &x, const Point &y, const Point &p)
Definition point.hpp:125
Point cross(const Point &x, const Point &y)
Definition point.hpp:35
double dot(const Point &x, const Point &y)
Definition point.hpp:57
double distanceToTriangle(const Point &x, const Point &y, const Point &z, const Point &p)
Definition point.hpp:146
DoublyLinkedEdgeList< T > type
std::vector< const T * > visiblePoints
std::vector< HalfEdgePtr > innerComponents