versor  3.0
C++11 library for Geometric algebra
vsr_hull.h
1 /*
2  * =====================================================================================
3  *
4  * Filename: vsr_hull.h
5  *
6  * Description: convex (and other assorted) hulls assembled into a half edge graph
7  *
8  * feed in ND Euclidean data, get out a graph connecting it. . .
9  *
10  * This works in Euclidean space, homogenizing it along the way to calculate half spaces,
11  *
12  * so it expects Euclidean input vectors (i.e. a vector of Euclidean vectors)
13  *
14  * Version: 1.0
15  * Created: 03/11/2014 18:10:36
16  * Revision: none
17  * Compiler: gcc
18  *
19  * Author: Pablo Colapinto (), gmail -> wolftype
20  * Organization:
21  *
22  * =====================================================================================
23  */
24 
25 
26 
27 #ifndef vsr_hull_INC
28 #define vsr_hull_INC
29 
30 #include "vsr_graph.h"
31 #include "detail/vsr_generic_op.h"
32 
33 namespace vsr{
34 
35 
40 template<bits::type DIM>
41 struct ConvexHull {
42 
43  typedef NEVec<DIM> Type;
45  typedef decltype( HomType() ^ HomType() ) EdgeType;
46  typedef HEGraph< Type > HEG;
47  typedef typename HEG::Face Face;
48  typedef typename HEG::HalfEdge Edge;
49 
51  HEG graph;
52 
53  // A BUNCH o' BOOLS to keep track of visitation
54  vector<bool> bVisited;
55 
56  /*-----------------------------------------------------------------------------
57  * Utilities. These sort of assume 3D for now ...
58  *-----------------------------------------------------------------------------*/
60  //template<bits::type DIM>
61  auto facetPlane( Face& f) RETURNS (
62  (euc::hom( f.a() ) ^ euc::hom (f.b()) ^ euc::hom( f.c() ) ).dual()
63  )
64 
66  //template<bits::type DIM>
67  auto edgeLine ( Edge& e) RETURNS(
68  euc::hom( e.a() ) ^ euc::hom( e.b() )
69  )
70 
72  //template<bits::type DIM>
73  auto edgePlane( Edge& ea, Edge& eb ) RETURNS (
74  (euc::hom( ea.a() ) ^ euc::hom (ea.b()) ^ euc::hom( eb.a() ) ).dual()
75  )
76 
78  template<class DualPlaneType>
79  bool isHalfSpace( DualPlaneType& dlp, vector<Type>& group ){
80  for (auto& i: group){
81  auto hi = euc::hom(i);
82  auto halfspace = (hi <= dlp)[0];
83  cout << halfspace << endl;
84  if (halfspace < -0.0001 ) return false;
85  }
86  return true;
87  }
88 
89  //Are a bunch of booleans true?
90  bool allTrue( vector<bool> b ){
91  for (int i = 0;i < b.size(); ++i){
92  if (!b[i]) {
93  return false;
94  };
95  }
96  return true;
97  }
98 
99 
100  /*-----------------------------------------------------------------------------
101  * MAKE CONVEX HULL GRAPH USING EVERY POINT IN GROUP
102  *-----------------------------------------------------------------------------*/
103  HEG& calc ( vector<Type>& group ) {
104 
105  graph.clear();
106  bVisited = vector<bool>( group.size(), false);
107 
108  initialFace(group);
109  convexPass(group);
110  closeHoles();
111 
112  return graph;
113 
114  }
115 
116  HEG& initialFace(vector<Type>& group){
117 
118  graph.clear();
119  bVisited = vector<bool>( group.size(), false);
120 
121  auto *ta = &group[0];
122  Type *tb, *tc;
123  int idxA, idxB, idxC;
124  idxA = 0;
125 
126  double min = 1000000;
127  int it = 0;
128  for (auto& i : group){
129  if( ta != &i){
130  auto dist = (*ta - i).wt();
131  if (dist < min) {
132  min = dist;
133  tb = &i;
134  idxB = it;
135  // cout << "new min " << dist << endl;
136  }
137  }
138  it++;
139  }
140 
141  auto edge = euc::hom(*ta) ^ euc::hom(*tb);
142 
143  it = 0;
144  for (auto& i : group){
145  if( (ta!=&i) && (tb!=&i) ){
146  auto plane = (edge ^ euc::hom(i)).dual();
147  // cout << "iter " << it << endl;
148  bool bShell = isHalfSpace(plane, group);
149  if (bShell) {
150  tc = &i;
151  idxC = it;
152  // cout << "halfspace found" << endl;
153  break;
154  }
155  }
156  it++;
157  }
158 
159 
160  // cout << idxA << " " << idxB << " " << idxC << endl;
161 
162  group[idxA].print();
163  group[idxB].print();
164  group[idxC].print();
165 
166  graph.add( group[idxA] ); bVisited[idxA] = true;
167  graph.add( group[idxB] ); bVisited[idxB] = true;
168  graph.add( group[idxC] ); bVisited[idxC] = true;
169 
170  /* return graph; */
171 
172  return graph;
173 
174  }
175 
176  /*-----------------------------------------------------------------------------
177  * FIRST pass TESTS every point to make SHALLOWEST change in CONVEX curvature
178  *-----------------------------------------------------------------------------*/
179  HEG& altConvexPass(vector<Type>& group){
180  //While all group members have not been accounted for . . .
181  bool bFoundNew = true;
182  while ( !allTrue( bVisited ) && bFoundNew ) {
183 
184  //Add Them to the graph
185  //
186  //Maybe no new vertices will be established
187  bFoundNew = false;
188  bool bUseEdgeB;
189 
190  auto face = graph.lastFace(); // Last Added Face
191  auto dlp = facetPlane( face ).unit(); // Plane of face (Normalized)
192 
193  auto& edgeA = graph.lastEdge(); // Last Added Edge
194  auto& edgeB = *(graph.lastEdge().next); // Next to Last Added Edge
195 
196  auto ea = euc::hom(edgeA.a());
197  auto eb = euc::hom(edgeA.b());
198  auto ec = euc::hom(edgeB.a());
199 
200  auto edgeLineA = ea ^ eb; // Last Edge Line
201  auto edgeLineB = ec ^ ea; // Alternative Edge
202 
203  // Keep track of idx of point allowing shallowest convex curve
204  VSR_PRECISION min = 0; VSR_PRECISION max = 10000; int idx;
205 
206  auto tdlp = dlp;
207  for (int i = 0; i < group.size(); ++i){
208 
209  if ( !bVisited[i] ) {
210 
211  auto n = euc::hom( group[i] ); // Homogenize
212 
213  // Candidate Plane
214  auto ndlp = (n ^ edgeLineA).dual().unit();
215  // Dot product must be POSITIVE to make convex hull
216  VSR_PRECISION convex = (ndlp <= dlp )[0];
217 
218  if (convex > min ){
219 
220  min = convex;
221  idx = i;
222  bUseEdgeB = false;
223  bFoundNew = true;
224  tdlp = ndlp;
225 
226  }
227 
228  // Try the other edge
229  ndlp = (n ^ edgeLineB).dual().unit();
230  convex = (ndlp <= dlp )[0];
231 
232  if (convex > min ){
233 
234  min = convex;
235  idx = i;
236  bUseEdgeB = true;
237  bFoundNew = true;
238  tdlp=ndlp;
239  }
240 
241  }
242  }
243 
244  //if legit
245  if ( isHalfSpace(tdlp, group) ) {
246 
247  //ADD in Point
248  if ( !bVisited[idx] && bFoundNew ){
249  //cout << "adding " << idx << endl;
250  if (!bUseEdgeB) graph.add( group[idx] );
251  else graph.addAt( group[idx], edgeB);
252  bVisited[idx] = true; //did it!
253  }
254  } else {
255  //otherwise, bfoundnew is false
256  bFoundNew = false;
257  }
258  }
259 
261 
262  //Try to hull any stragglers
263  for(int it=0; it < bVisited.size(); ++it){
264  if (!bVisited[it]){
265  //try some edge
266  for (auto& e : graph.edge() ){
267  auto dlp = ( edgeLine(*e) ^ euc::hom( group[it] ) ).dual(); // Dual plane
268  if ( isHalfSpace(dlp, group) ) {
269  //success, add point to edge
270  graph.addAt( group[it], *e );
271  bVisited[it] = true;
272  break;
273  }
274  }
275  }
276  }
277  return graph;
278  }
279 
280 
281  /*-----------------------------------------------------------------------------
282  * FIRST pass TESTS every point to make SHALLOWEST change in CONVEX curvature
283  *-----------------------------------------------------------------------------*/
284  HEG& convexPass(vector<Type>& group){
285  //While all group members have not been accounted for . . .
286  bool bFoundNew = true;
287  while ( !allTrue( bVisited ) && bFoundNew ) {
288 
289  //Add Them to the graph
290  //
291  //Maybe no new vertices will be established
292  bFoundNew = false;
293  bool bUseEdgeB;
294 
295  auto face = graph.lastFace(); // Last Added Face
296  auto dlp = facetPlane( face ).unit(); // Plane of face (Normalized)
297 
298  auto& edgeA = graph.lastEdge(); // Last Added Edge
299  auto& edgeB = *(graph.lastEdge().next); // Next to Last Added Edge
300 
301  auto ea = euc::hom(edgeA.a());
302  auto eb = euc::hom(edgeA.b());
303  auto ec = euc::hom(edgeB.a());
304 
305  auto edgeLineA = ea ^ eb; // Last Edge Line
306  auto edgeLineB = ec ^ ea; // Alternative Edge
307 
308  // Keep track of idx of point allowing shallowest convex curve
309  VSR_PRECISION min = 0; VSR_PRECISION max = 10000; int idx;
310 
311  for (int i = 0; i < group.size(); ++i){
312 
313  if ( !bVisited[i] ) {
314 
315  auto n = euc::hom( group[i] ); // Homogenize
316 
317  // Candidate Plane
318  auto ndlp = (n ^ edgeLineA).dual().unit();
319  // Dot product must be POSITIVE to make convex hull
320  VSR_PRECISION convex = (ndlp <= dlp )[0];
321 
322  if (convex > 0 ){
323 
324  // Distance to last added plane:
325  VSR_PRECISION dist = fabs( (n <= dlp)[0] );
326 
327  // Higher dot product == shallower change in curvature
328  // Lower distance to original plane means ?
329  if ( convex > min && dist < max ) {
330 
331  min = convex;
332  max = dist;
333  idx = i;
334  bUseEdgeB = false;
335  bFoundNew = true;
336 
337  }
338 
339  } else {
340 
341  // Try the other edge
342  ndlp = (n ^ edgeLineB).dual().unit();
343  convex = (ndlp <= dlp )[0];
344 
345  if (convex > 0 ){
346 
347  VSR_PRECISION dist = fabs( (n <= dlp)[0] );
348 
349  if ( convex > min && dist < max ) {
350 
351  min = convex;
352  max = dist;
353  idx = i;
354  bUseEdgeB = true;
355  bFoundNew = true;
356  }
357  }
358  }
359  }
360  }
361 
362  //ADD in Point
363  if ( !bVisited[idx] && bFoundNew ){
364  //cout << "adding " << idx << endl;
365  if (!bUseEdgeB) graph.add( group[idx] );
366  else graph.addAt( group[idx], edgeB);
367  bVisited[idx] = true; //did it!
368  }
369  }
370 
371  //Try to hull any stragglers
372  for(int it=0; it < bVisited.size(); ++it){
373  if (!bVisited[it]){
374  //try some edge
375  for (auto& e : graph.edge() ){
376  auto dlp = ( edgeLine(*e) ^ euc::hom( group[it] ) ).dual(); // Dual plane
377  if ( isHalfSpace(dlp, group) ) {
378  //success, add point to edge
379  graph.addAt( group[it], *e );
380  bVisited[it] = true;
381  break;
382  }
383  }
384  }
385  }
386  return graph;
387  }
388 
389 
390 
391  /*-----------------------------------------------------------------------------
392  * CLOSE HOLES IN TOPOLOGY
393  *-----------------------------------------------------------------------------*/
394  HEG& closeHoles(int N=0){
395  int iter = 0;
396  while ( !graph.nullEdges().empty() && iter<N ) {
397  iter++;
398 
399  auto nullEdges = graph.nullEdges();
400 
401  //cout << nullEdges.size() << " null edges " << endl;
402  // First nullEdge
403  auto& ea = nullEdges[0];
404  // Edge and Face geometric data
405  auto ela = edgeLine(*ea);
406  auto dlp = facetPlane( *( ea->face ) ).unit();
407 
408 
409 
410  // Find another edge with which to make new facet
411  Edge * eb;
412 
413  VSR_PRECISION min = 0;
414  bool bFound = false;
415  for (auto& i : nullEdges ){
416 
417  //1. Ignore edges that point to same node
418  if (ea->node != i->node){
419 
420  //2. Try simply sealing it up first (maybe edges share nodes)
421  if ( ea->isOpp(*i) ) {
422  ea->seal(*i);
423  bFound = true;
424  break; //break if found
425  }
426 
427  //3. Otherwise keep track of shallowest, most convex match
428  auto n = euc::hom( i->a() );
429  auto ndlpA = ( n ^ ela ).dual().unit();
430  auto ndlpB = ( ela ^ n ).dual().unit();
431  VSR_PRECISION convex = (ndlpA <= dlp)[0];
432  cout << "test convex: " << convex << " " << (dlp<=ndlpB)[0] << endl;
433  if (convex > min ){
434  min = convex;
435  eb = i;
436  cout << "convex: " << min << endl;
437  }
438  }
439 
440  }
441 
442  // Called only if simple seal not found
443  if (!bFound){
444  bool bTestBoth = false;
445  //Do ea and eb share a node already?
446  if ( ea->ccwFrom( *eb ) ){
447  //Are we closing a triangle?
448  if ( ea -> triangle() ) { graph.close( *ea ); }
449  //Otherwise close ccw
450  else { graph.close( *ea, *eb ); cout << "ccw" << endl; }
451  } else if (ea->cwFrom( *eb )){
452  //Are we closing a triangle?
453  if ( ea -> triangle() ) { graph.close( *ea ); }
454  //Otherwise close cc
455  else {graph.close( *eb, *ea ); cout << "cc" << endl; }
456  } else {
457  //Or just add a point
458  graph.close( *ea, *(eb -> node) );
459  cout << "pt" << endl;
460  bTestBoth = true;
461  }
462 
463  // cout << "made face #: " << graph.face().size() -1 << endl;
464 
465  //Now try simple seal of new edges
466  for (auto& i : nullEdges ){
467  if ( graph.edge( -1 ).isOpp(*i) ) {
468  graph.edge( -1 ).seal(*i);
469  cout << "suture 1" << endl;
470  break;
471  }
472  }
473  //Test on both new edges if closed to a point
474  if (bTestBoth){
475  for (auto& i : nullEdges ){
476  if ( graph.edge( -3 ).isOpp(*i) ) {
477  graph.edge( -3 ).seal(*i);
478  cout << "suture 2" << endl;
479  break;
480  }
481  }
482  }
483  }
484 
485  }
486  return graph;
487  }
488 
489 // ConvexHull
490 };
491 
492 } // vsr::
493 
494 #endif /* ----- #ifndef vsr_hull_INC ----- */
NEVec< DIM+1 > HomType
Homogenized Vector type.
Definition: vsr_hull.h:44
auto edgeLine(Edge &e) -> auto
Make direct line from an edge.
Generic Geometric Number Types (templated on an algebra and a basis )
Definition: vsr_algebra.h:69
vector< HalfEdge * > nullEdges()
Get null edges of graph.
Definition: vsr_graph.h:674
auto facetPlane(Face &f) -> auto
Make dual euclidean plane from a face.
Templated half edge structure (pointers to any type) Navigates references to surface topology of data...
Definition: vsr_graph.h:18
core namespaced operations that are metric-agnostic
HEG graph
The half edge graph.
Definition: vsr_hull.h:51
bool isHalfSpace(DualPlaneType &dlp, vector< Type > &group)
are all members of group in same half space relative to plane?
Definition: vsr_hull.h:79
the versor library namespace
Definition: vsr_algebra.h:29
auto edgePlane(Edge &ea, Edge &eb) -> auto
Make plane from two edges.
convex (and other assorted) hulls assembled into a half edge graph
Definition: vsr_hull.h:41
void close(HalfEdge &ha, HalfEdge &hb)
eb /ea counter clockwise
Definition: vsr_graph.h:434
NEVec< DIM > Type
Vector type.
Definition: vsr_hull.h:43