cheshirekow  v0.1.0
induced_subcomplex.hpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2014 Josh Bialkowski (jbialk@mit.edu)
3  *
4  * This file is part of mpblocks.
5  *
6  * mpblocks is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * mpblocks is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with mpblocks. If not, see <http://www.gnu.org/licenses/>.
18  */
26 #ifndef EDELSBRUNNER96_INDUCED_SUBCOMPLEX_HPP_
27 #define EDELSBRUNNER96_INDUCED_SUBCOMPLEX_HPP_
28 
30 
31 #include <cassert>
32 #include <list>
33 #include <map>
34 #include <set>
35 
37 
38 namespace edelsbrunner96 {
39 
40 template<typename Traits>
42  SimplexRef s1_ref) {
43  /* s0,s1 scope */{
44  // convenient references for the following two set operations (scoped to
45  // avoid confusion)
46  Simplex& s0 = storage[s0_ref];
47  Simplex& s1 = storage[s1_ref];
48 
49  // TODO(bialkowski): potential optimization: combine these two scans
50  std::set_union(s0.V.begin(), s0.V.end(), s1.V.begin(), s1.V.end(),
51  V.begin());
52 
53  // Note: e[0] is the vertex missing from s0 and e[1] is the vertex
54  // missing from s1, which is why e+1 comes before e+0 in the output
55  // iterators
56  set::IntersectionAndDifference(s0.V.begin(), s0.V.end(), s1.V.begin(),
57  s1.V.end(), e.begin() + 1, e.begin(),
58  f.begin());
59  }
60 
61  // queue up the first two simples
62  s[0] = s0_ref;
63  s[1] = s1_ref;
64 }
65 
66 template <typename Traits>
68  const std::array<SimplexRef, 2>& s) {
69  Init(storage, s[0], s[1]);
70 }
71 
72 template<class Traits>
74  typedef Eigen::Matrix<Scalar, Traits::NDim, 1> VectorN;
75 
76  // if the other point is not in this sphere then it is locally reguarly,
77  // otherwise it is not
78  bool isRegular[2];
79  for (int i = 0; i < 2; i++) {
80  VectorN v = storage[e[i]] - storage[s[i]].c;
81  isRegular[i] = v.squaredNorm() > storage[s[i]].r2;
82  }
83 
84  return isRegular[0] && isRegular[1];
85 }
86 
87 template<typename Traits>
89  // initialize S with null references
90  for (int i = 0; i < Traits::NDim + 2; i++) {
91  S[i] = storage.NullSimplex();
92  }
93 
94  // the set of simplices queued for expansion
95  std::list<BreadthFirstNode> bfs_queue;
96 
97  // the set of simplices that have been expanded (so that we can clear the
98  // mark)
99  std::list<SimplexRef> evaluated_list;
100 
101  for (int i = 0; i < 2; i++) {
102  SimplexRef s_ref = s[i];
103  Simplex& s = storage[s_ref];
104  PointRef v_missing = e[i];
105  uint8_t i_of_missing =
106  std::lower_bound(V.begin(), V.end(), v_missing) - V.begin();
107  assert(i_of_missing < Traits::NDim + 2);
108  s.marked[simplex::ISC_OPENED] = true;
109  evaluated_list.push_back(s_ref);
110  bfs_queue.emplace_back(s_ref, v_missing);
111  S[i_of_missing] = s_ref;
112  }
113 
114  while (bfs_queue.size() > 0) {
115  BreadthFirstNode bfs_node = bfs_queue.front();
116  bfs_queue.pop_front();
117 
118  for (int i = 0; i < Traits::NDim + 1; i++) {
119  // Since neighbor is a simplex adjacent to bfs_node.s_ref, it shares
120  // all but one vertex with bfs_node.s_ref. If that vertex it doesn't
121  // share is the one of the facet that bfs_node.s_ref is missing, then
122  // neighbor is also in the induced subcomplex, and the vertex it is
123  // missing is s_ref.V[i].
124  SimplexRef neighbor = storage[bfs_node.s_ref].N[i];
125  PointRef v_missing = storage[bfs_node.s_ref].V[i];
126 
127  // TODO(bialkowski): If we choose to deal with boundary simplices by
128  // null simplex references, then we need to watch out for them here
129 // if( neighbor == storage.NullSimplex() ) {
130 // continue;
131 // }
132 
133  Simplex& s_neighbor = storage[neighbor];
134  if (!s_neighbor.marked[simplex::ISC_OPENED]) {
135  s_neighbor.marked[simplex::ISC_OPENED] = true;
136  evaluated_list.push_back(neighbor);
137 
138  // as mentioned above, if we can find the vertex that bfs_node.s_ref is
139  // missing in s_neighbor then s_neighbor is also in the induced complex
140  if (std::binary_search(s_neighbor.V.begin(), s_neighbor.V.end(),
141  bfs_node.v_missing)) {
142  // the index in induced_subcomplex.V of the vertex that is missing
143  // from s_neighbor
144  uint8_t i_of_missing =
145  std::lower_bound(V.begin(), V.end(), v_missing) - V.begin();
146  assert(i_of_missing < Traits::NDim + 2);
147  S[i_of_missing] = neighbor;
148  bfs_queue.emplace_back(neighbor, v_missing);
149  }
150  }
151  }
152  }
153 
154  // clear makers
155  for (SimplexRef s_ref : evaluated_list) {
156  storage[s_ref].marked[simplex::ISC_OPENED] = false;
157  }
158 }
159 
160 template <typename T> int signum(T val) {
161  return (T(0) < val) - (val < T(0));
162 }
163 
164 template<typename Traits>
166  typedef typename Traits::Scalar Scalar;
167  typedef Eigen::Matrix<Scalar, Traits::NDim, Traits::NDim> Matrix;
168  typedef Eigen::Matrix<Scalar, Traits::NDim, 1> Vector;
169 
170  // pre-fill the matrix used for computing hyperplanes
171  Matrix A;
172  for (int i = 0; i < Traits::NDim; i++) {
173  A.row(i) = storage[f[i]];
174  }
175 
176  // and the offset vector, the only requirement is that all the elements
177  // of b are the same, (i.e. all points are the same distance from the
178  // hyperplane along the normal), however by using the max coefficient of A
179  // I have the vague notion that solving the system will have better
180  // numerical stability
181  Vector b;
182  b.fill(1.0);
183 
184  // for each (NDim-1)simplex on the edge of the facet
185  for (int i = 0; i < Traits::NDim; i++) {
186  // Create the hyperplane composed of this edge and one of the
187  // non-facet vertices (i.e. endpoints) of the simplex pair
188 
189  // Start by replacing the i'th row of A with one of the endpoints. We
190  // arbitrarily choose e[0], though e[1] would work as well, as this test
191  // is symmetric.
192  A.row(i) = storage[e[0]];
193 
194  // We will compute the normal and offset from the removed facet point
195  Matrix B;
196  for(int j=0; j < Traits::NDim; j++) {
197  B.row(j) = storage[f[i]];
198  }
199 
200  // Now solve for the normal and offset of the hyperplane coincident to
201  // this facet.
202  Vector n = (A-B).fullPivLu().solve(b).normalized();
203  Scalar o = (storage[e[0]] - storage[f[i]]).dot(n);
204  if( o < 0 ) {
205  n = -n;
206  o = -o;
207  }
208 
209  // now restore the row of A that we replaced
210  A.row(i) = storage[f[i]];
211 
212  // Now check if the two other vertices of V are on the same side of this
213  // facet, (i.e. the facet is a hull facet of V). If they're both on the
214  // same side then the edge is convex and we may continue
215  if (n.dot(storage[e[1]] - storage[f[i]]) < o) {
216  continue;
217  }
218 
219  // if they're not on the same side, then the edge is reflex, and
220  // we must verify that it is incident only to members of the induced
221  // subcomplex, this is the same as saying the S0 and S1 share the
222  // same neighbor across from the point V[i] and that neighbor is
223  // in the induced subcomplex.
224  SimplexRef neighbors[2];
225  for (int j = 0; j < 2; j++) {
226  neighbors[j] = storage[s[j]].NeighborAcross(f[i]);
227  }
228 
229  // If the two simplices do not share the neighbor across this edge then
230  // the facet is not flippable;
231  if (neighbors[0] != neighbors[1]) {
232  return false;
233  }
234 
235  // If the two share a neighbor across this point, then that neighbor
236  // must be part of the induced subcomplex. This is because the neighbor
237  // simplex is formed by d-1 edge vertices of the facet plus the two
238  // endpoints (one from each bounding simplex). Therefore it must be
239  // part of the induced subcomplex, and we do not even need to check if
240  // neighbor is in S;
241  }
242 
243  return true;
244 }
245 
246 template<typename Traits>
248  typename Traits::Simplex& s) {
249  uint8_t i = 0;
250  uint8_t j = 0;
251 
252  while (i < Traits::NDim + 1 && j < Traits::NDim + 2) {
253  while (V[j] < s.V[i]) {
254  j++;
255  }
256 
257  if (s.V[i] < V[j]) {
258  return i;
259  }
260  ++i;
261  ++j;
262  }
263 
264  return i;
265 }
266 
267 /*
268  * There are two ways we can accomplish the flip. The geometric way, and
269  * the graph way.
270  *
271  * The geometric way:
272  * 1) lift and invert all the points
273  * 2) for each lifted d+1 subset of points determine the hyperplane in
274  * R^(d+1) that is coincident to those points
275  * 3) use the "other" point to orient the face away from the simplex
276  * 4) determine if the center of inversion is visible or not from that
277  * face
278  * 5) separate the set of faces into visible/not-visible
279  * 6) determine which one corresponds to our current triangulation
280  * 7) replace it with the new one
281  *
282  * The graph way (not particularly intuitive, but very clean):
283  * There are only NDim+2 possible simplices that can be formed by a set of
284  * NDim+2 vertices (NDim+2 choose NDim+1). We already have some of them in
285  * the current triangulation (non-null elements of S), so we just go through
286  * and replace them with the Ndim+2 - |S| ones that aren't there. So, we go
287  * through each combination of Ndim+1 vertices. If it is already a simplex in
288  * the current triangulation, then remove it. If it is not already a simplex,
289  * then add it to the triangulation.
290  */
291 template<typename Traits>
293  // Before modifying any simplices, build the neighborhood of the induced
294  // subcomplex. Each hull facet of the subcomplex is found by removing two
295  // vertices from the hull. Because the subcomplex is flippable, any
296  // neighbor of a simplex in the induced subcomplex is either unique to that
297  // simplex or else a member of the subcomplex.
298 
299  // TODO(bialkowski): sparse map or dense map? pay with storage or time?
300  Eigen::Array<SimplexRef, Traits::NDim + 2, Traits::NDim + 2> neighborhood;
301  neighborhood.fill(storage.NullSimplex());
302 
303  for (int i = 0; i < Traits::NDim + 2; i++) {
304  if (S[i] == storage.NullSimplex()) {
305  continue;
306  }
307  // If the neighbor of S[i] across V[j] is part of the subcomplex, then it
308  // must be missing vertex j, so it must be in slot S[j].
309  for (int j = 0; j < Traits::NDim + 2; j++) {
310  if(i == j) {
311  continue;
312  }
313 
314  // if S[j] is empty then the neighbor of S[i] across V[j] is not in the
315  // induced subcomplex and is therefore a neighbor of the induced
316  // subcomplex.
317  if (S[j] == storage.NullSimplex()) {
318  SimplexRef neighbor = storage[S[i]].NeighborAcross(V[j]);
319  neighborhood(i, j) = neighbor;
320  neighborhood(j, i) = neighbor;
321  }
322  }
323  }
324 
325  // We simply iterate over S and flip each simplex
326  for (int i = 0; i < Traits::NDim + 2; i++) {
327  // If the simplex does not exist, then create it
328  if (S[i] == storage.NullSimplex()) {
329  // retrieve a new simplex object
330  S[i] = storage.Promote();
331 
332  // it's vertices are composed of all vertices except for the i'th
333  // Note(bialkowski): No need to sort since V itself is sorted.
334  std::remove_copy_if(V.begin(), V.end(), storage[S[i]].V.begin(),
335  [this,i](PointRef x) {return x == V[i];});
336 
337  // now that it has all it's vertices we can compute the
338  // circumcenter
339  storage[S[i]].ComputeCenter(storage);
340 
341  // If the simplex exists, then destroy it
342  } else {
343  storage.Retire(S[i]);
344  S[i] = storage.NullSimplex();
345  }
346  }
347 
348  // Now we assign neighbors. The simplex at S[i] is missing the vertex V[i].
349  // It's j'th neighbor contains V[i], and is missing S[i].V[j]
350  for (int i = 0; i < Traits::NDim + 2; i++) {
351  if (S[i] == storage.NullSimplex()) {
352  continue;
353  }
354  for (int j = 0; j < Traits::NDim + 2; j++) {
355  if (i == j) {
356  continue;
357  }
358  // if S[j] is not in the current triangulation then the neighbor across
359  // from V[j] must be a neighbor of the induced subcomplex
360  if (S[j] == storage.NullSimplex()) {
361  SimplexRef neighbor = neighborhood(i, j);
362  Simplex& s_neighbor = storage[neighbor];
363  storage[S[i]].SetNeighborAcross(V[j], neighborhood(i, j));
364 
365  uint8_t k = FindIndexOfPeakVertex(s_neighbor);
366  storage[neighbor].N[k] = S[i];
367  } else {
368  storage[S[i]].SetNeighborAcross(V[j], S[j]);
369  }
370  }
371  }
372 }
373 
374 } // namespace edelsbrunner96
375 
376 #endif // EDELSBRUNNER96_INDUCED_SUBCOMPLEX_HPP_
void IntersectionAndDifference(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, OutputIt1 out1, OutputIt2 out2, OutputIt3 intersect)
Collect in out1 elements from the first set not found in the second, and collect in out2 elements fro...
bool IsFlippable(Storage &storage)
Return true if the facet is flippable.
uint8_t FindIndexOfPeakVertex(Simplex &s)
Given a simplex s which borders the hull of V (i.e. s shares a facet with hull(V)), then find the vertex of s which is not in V, and return it's index in S.V;.
bool IsLocallyRegular(Storage &storage)
Return true if the facet is locally regular.
__device__ __host__ Scalar dot(const RValue< Scalar, ROWS, 1, ExpA > &A, const RValue< Scalar, ROWS, 1, ExpB > &B)
compute the DOT
Definition: Dot.h:84
void Build(Storage &storage)
Fill the induced simplex by breadth-first search of the neighborhood about s[0] and s[1]...
queued in induced subcomplex search
Definition: simplex.h:49
void Init(Storage &storage, SimplexRef s0_ref, SimplexRef s1_ref)
Initialize the data structure with the two simplices and build the vertex set for the common facet...
We do a breadthfirst search for simplices in the induced subcomplex. This data structure is what we q...
void Flip(Storage &storage)
Flip the subcomplex.