cheshirekow  v0.1.0
line_walker.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_LINE_WALKER_HPP_
27 #define EDELSBRUNNER96_LINE_WALKER_HPP_
28 
30 
31 #include <list>
32 #include <vector>
34 
35 namespace edelsbrunner96 {
36 
41 template<typename Traits>
42 typename Traits::SimplexRef LineWalk(typename Traits::Storage& storage,
43  typename Traits::SimplexRef s0_ref,
44  const typename Traits::Point& pt) {
45  typedef typename Traits::Scalar Scalar;
46  typedef typename Traits::PointRef PointRef;
47  typedef typename Traits::SimplexRef SimplexRef;
48  typedef typename Traits::Simplex Simplex;
49 
50  typedef Eigen::Matrix<Scalar, Traits::NDim, Traits::NDim> MatrixN;
51  typedef Eigen::Matrix<Scalar, Traits::NDim, 1> VectorN;
52 
53  Simplex& s0 = storage[s0_ref];
54  SimplexRef result = storage.NullSimplex();
55 
56  // median point of the starting simplex
57  VectorN ps = VectorN::Zero();
58  for (PointRef pr : s0.V) {
59  ps += (1.0 / (Traits::NDim + 1)) * storage[pr];
60  }
61 
62  // queue for the walk
63  typedef std::pair<Scalar, SimplexRef> QueueElement;
64  std::list<QueueElement> simplex_queue;
65 
66  // list of simplices we have already queued for walking (or already walked)
67  std::list<SimplexRef> marked;
68 
69  // mark the initial simplex and queue it up
70  s0.marked[simplex::LINE_WALK] = true;
71  marked.push_back(s0_ref);
72  simplex_queue.push_back(QueueElement(0, s0_ref));
73 
74  // while the simplex queue is not empty, expand simplices
75  while (!simplex_queue.empty()) {
76  QueueElement e = simplex_queue.front();
77  Scalar s_min = e.first;
78  SimplexRef simplex_ref = e.second;
79  Simplex& simplex = storage[simplex_ref];
80  simplex_queue.pop_front();
81 
82  // if this is an infinite simplex then just return it, as the walk has
83  // left the triangulation (i.e. the query point is outside the current
84  // hull)
85  if (simplex.V[0] == storage.NullPoint()) {
86  result = simplex_ref;
87  break;
88  }
89 
90  // for each facet, find the normal and offset relative to the starting
91  // point, and compute the distance from the starting point to that plane
92  // along the line segment we are traversing
93  typedef std::pair<Scalar, int> FacetElement;
94  typedef std::set<FacetElement> FacetSet;
95  FacetSet facet_set;
96 
97  for (int i = 0; i < Traits::NDim + 1; i++) {
98  MatrixN A;
99  VectorN b;
100  int k = 0;
101  for (int j = 0; j < Traits::NDim + 1; j++) {
102  if (j == i) {
103  continue;
104  }
105  A.row(k++) = storage[simplex.V[j]] - ps;
106  }
107  b.fill(1);
108  VectorN normal = A.fullPivLu().solve(b).normalized();
109  Scalar offset = normal.dot(A.row(0));
110  // orient the hyperplane so that it points "away" from the starting
111  // point
112  if (offset < 0) {
113  normal = -normal;
114  offset = -offset;
115  }
116 
117  // compute the intersection of the facet with the line segment
118  Scalar s = offset / (pt - ps).dot(normal);
119 
120  // if the intersection is not in the right direction, then skip it
121  // Note(bialkowski): Since a vertices of a facet are ordered, and the
122  // normal/offset of the vertex are always computed with respect to the
123  // start point, s-value of the facet over which we crossed to enter this
124  // simplex will always be computed the same as when it was evaluated
125  // in the previous simplex. I.e. the A matrix is identical, the b-matrix
126  // is identical, so there is no possibility of numerical stability issues.
127  if (s <= s_min) {
128  continue;
129  }
130  facet_set.insert(FacetElement(s, i));
131  }
132 
133  // if no facets are found, then just continue
134  if (facet_set.empty()) {
135  continue;
136  }
137 
138  FacetElement first_element = *facet_set.begin();
139  if (first_element.first > 1) {
140  result = simplex_ref;
141  break;
142  }
143 
144  // Starting with the first facet that we hit in this direction,
145  // queue neighbors across all facets that are numerically the same distance
146  // along this direction
147  for (const FacetElement element : facet_set) {
148  if (std::abs(element.first - first_element.first) < 1e-9) {
149  SimplexRef neighbor_ref = simplex.N[element.second];
150  Simplex& neighbor = storage[neighbor_ref];
151  if (!neighbor.marked[simplex::LINE_WALK]) {
152  simplex_queue.push_back(QueueElement(element.first, neighbor_ref));
153  neighbor.marked[simplex::LINE_WALK] = true;
154  marked.push_back(neighbor_ref);
155  }
156  }
157  }
158  }
159 
160  // clear markers
161  for (SimplexRef s : marked) {
162  storage[s].marked[simplex::LINE_WALK] = false;
163  }
164  return result;
165 }
166 
167 template <typename Traits, typename InputIterator, typename OutputIterator>
168 void FeatureWalk(typename Traits::Storage& storage,
169  typename Traits::SimplexRef s_0,
170  InputIterator Vf_begin, InputIterator Vf_end,
171  OutputIterator out) {
172  typedef typename Traits::PointRef PointRef;
173  typedef typename Traits::SimplexRef SimplexRef;
174 
175  // number of vertices in the feature. The dimensonality of the feature is
176  // n_vertices-1.
177  int n_vertices = (Vf_end - Vf_begin);
178 
179  // queue of simplices to expand. We push new simplices to the back of the
180  // queue, but we do not pop them from the front. Instead we maintain an
181  // iterator into the queue up-to-which we have already processed. This way
182  // we have a full list of marked simplices that we can unmark when we are
183  // finished.
184  std::list<SimplexRef> bfs_queue;
185  storage[s_0].marked[simplex::FEATURE_WALK] = true;
186  bfs_queue.push_back(s_0);
187 
188  // Vertices that are not part of the feature
189  std::vector<PointRef> V;
190  V.reserve(Traits::NDim + 1 - n_vertices);
191 
192  for (auto queue_iter = bfs_queue.begin(); queue_iter != bfs_queue.end();
193  ++queue_iter) {
194  SimplexRef s_ref = *queue_iter;
195 
196  // output this simplex
197  *out++ = s_ref;
198 
199  // Store in V the sorted vertex references which are not part of the
200  // common feature
201  V.clear();
202  std::set_difference(storage[s_ref].V.begin(),
203  storage[s_ref].V.end(),
204  Vf_begin, Vf_end, std::back_inserter(V));
205 
206  // verify that the feature is actually a feature of this simplex
207  assert(V.size() + n_vertices == Traits::NDim+1);
208 
209  // for each vertex in that set, queue up the neighbor across that vertex,
210  // unless it has already been queued.
211  for (PointRef v : V) {
212  SimplexRef neighbor_ref = storage[s_ref].NeighborAcross(v);
213  if (!storage[neighbor_ref].marked[simplex::FEATURE_WALK]) {
214  storage[neighbor_ref].marked[simplex::FEATURE_WALK] = true;
215  bfs_queue.push_back(neighbor_ref);
216  }
217  }
218  }
219 
220  // clear the mark
221  for(SimplexRef s_ref : bfs_queue) {
222  storage[s_ref].marked[simplex::FEATURE_WALK] = false;
223  }
224 }
225 
226 template <typename Traits, class OutputIterator>
227 void FuzzyWalk_(typename Traits::Storage& storage,
228  const typename Traits::SimplexRef s_0,
229  const typename Traits::Point& x_q,
230  const typename Traits::Scalar epsilon,
231  std::list<typename Traits::SimplexRef>& search_queue,
232  OutputIterator out) {
233  typedef typename Traits::SimplexRef SimplexRef;
234  typedef typename Traits::Scalar Scalar;
235  typedef typename Traits::Point Point;
236  typedef typename Traits::PointRef PointRef;
237 
238  storage[s_0].marked[simplex::FUZZY_WALK] = true;
239  search_queue.push_back(s_0);
240 
241  std::vector<PointRef> V_feature;
242  V_feature.reserve(Traits::NDim+1);
243  Point x_proj;
244  for (auto queue_iter = search_queue.begin(); queue_iter != search_queue.end();
245  ++queue_iter) {
246  V_feature.clear();
247  Scalar dist = SimplexDistance<Traits>(storage, x_q, *queue_iter, &x_proj,
248  std::back_inserter(V_feature));
249 
250  // If the walk reaches a visible hull simplex, then we switch to a hull
251  // walk to get all visible hull simplices
252  if (storage[*queue_iter].V[0] == storage.NullPoint()) {
253  // by convention, for an infinite simplex the SimplexDistance is the
254  // distance to the hull facet, so we include it in the output set if
255  // it is either visible or within epsilon distance of the query
256  if (dist < epsilon || IsVisible<Traits>(storage, *queue_iter, x_q)) {
257  *out++ = *queue_iter;
258  // Switch to breadth-first search for visible hull simplices
259  for (SimplexRef s_ref : storage[*queue_iter].N) {
260  if (!storage[s_ref].marked[simplex::FUZZY_WALK]) {
261  storage[s_ref].marked[simplex::FUZZY_WALK] = true;
262  search_queue.push_back(s_ref);
263  }
264  }
265  }
266  } else {
267  // If the simplex is within the fuzz distance, switch to breadth-first
268  // search
269  if (dist < epsilon) {
270  *out++ = *queue_iter;
271  for (SimplexRef s_ref : storage[*queue_iter].N) {
272  if (!storage[s_ref].marked[simplex::FUZZY_WALK]) {
273  storage[s_ref].marked[simplex::FUZZY_WALK] = true;
274  search_queue.push_back(s_ref);
275  }
276  }
277  // Otherise walk the nearest feature (depth-first)
278  } else {
279  std::list<SimplexRef> link;
280  std::sort(V_feature.begin(), V_feature.end());
281  FeatureWalk<Traits>(storage, *queue_iter, V_feature.begin(),
282  V_feature.end(), std::back_inserter(link));
283  for (SimplexRef s_ref : link) {
284  if (!storage[s_ref].marked[simplex::FUZZY_WALK]) {
285  storage[s_ref].marked[simplex::FUZZY_WALK] = true;
286  search_queue.push_back(s_ref);
287  }
288  }
289  }
290  }
291  }
292 
293  for(SimplexRef s_ref : search_queue) {
294  storage[s_ref].marked[simplex::FUZZY_WALK] = false;
295  }
296 }
297 
298 template <typename Traits, class OutputIterator>
299 void FuzzyWalk(typename Traits::Storage& storage,
300  const typename Traits::SimplexRef s_0,
301  const typename Traits::Point& x_q,
302  const typename Traits::Scalar epsilon,
303  OutputIterator out) {
304  typedef typename Traits::SimplexRef SimplexRef;
305  std::list<SimplexRef> search_queue;
306  FuzzyWalk_<Traits>(storage, s_0, x_q, epsilon, search_queue, out);
307 }
308 
309 template <class Traits>
310 bool IsVisible(typename Traits::Storage& storage,
311  typename Traits::SimplexRef s_ref,
312  const typename Traits::Point& x_q,
313  typename Traits::Scalar epsilon) {
314  typedef typename Traits::Scalar Scalar;
315  typedef typename Traits::PointRef PointRef;
316  typedef typename Traits::Point Point;
317  typedef typename Traits::SimplexRef SimplexRef;
318  typedef typename Traits::Simplex Simplex;
319 
320  Simplex& simplex = storage[s_ref];
321  SimplexRef interior_ref = simplex.N[0];
322  Simplex& interior_simplex = storage[interior_ref];
323 
324  // find the index in the interior simplex vertex set of the vertex
325  // which is not part of the common facet
326  PointRef interior_peak_ref;
327  std::set_difference(interior_simplex.V.begin(), interior_simplex.V.end(),
328  simplex.V.begin(), simplex.V.end(), &interior_peak_ref);
329 
330  // compute the normal and offset of the facet with respect to this
331  // designated interior vertex
332  typedef Eigen::Matrix<Scalar, Traits::NDim, Traits::NDim> MatrixN;
333  typedef Eigen::Matrix<Scalar, Traits::NDim, 1> VectorN;
334  const Point& interior_point = storage[interior_peak_ref];
335  MatrixN A;
336  for (int i = 1; i < Traits::NDim + 1; i++) {
337  A.row(i - 1) = storage[simplex.V[i]] - interior_point;
338  }
339  VectorN b;
340  b.fill(1.0);
341  VectorN normal = A.fullPivLu().solve(b).normalized();
342  Scalar offset = normal.dot(A.row(0));
343 
344  // orient the plane so that it "points away" from the interior or
345  // the point set
346  if (offset < 0) {
347  normal = -normal;
348  offset = -offset;
349  }
350 
351  // now test the query point for visibility
352  return (normal.dot(x_q - interior_point) - offset > -epsilon);
353 }
354 
355 template<class Traits, class OutputIterator>
357  typename Traits::Storage& storage, typename Traits::SimplexRef s_0,
358  const typename Traits::Point& x_q,
359  OutputIterator out) {
360  typedef typename Traits::Scalar Scalar;
361  typedef typename Traits::PointRef PointRef;
362  typedef typename Traits::Point Point;
363  typedef typename Traits::SimplexRef SimplexRef;
364  typedef typename Traits::Simplex Simplex;
365 
366  typedef Eigen::Matrix<Scalar, Traits::NDim, Traits::NDim> MatrixN;
367  typedef Eigen::Matrix<Scalar, Traits::NDim, 1> VectorN;
368 
369  std::list<SimplexRef> visible_list;
370  std::list<SimplexRef> marked_list; // evaluated simplices
371 
372  // initialize the queue
373  visible_list.push_back(s_0);
374  storage[s_0].marked[simplex::VISIBLE_HULL_WALK] = true;
375  marked_list.push_back(s_0);
376 
377  typename std::list<SimplexRef>::iterator iter = visible_list.begin();
378  for (; iter != visible_list.end(); ++iter) {
379  SimplexRef simplex_ref = *iter;
380  Simplex& simplex = storage[simplex_ref];
381 
382  // for each un-evaluated neighbor, evaluate visibility, and if it is
383  // visible add it to the result set and queue it for expansion.
384  // Note(bialkowski): Because the designated NullPoint is assumed to
385  // compare less than all other point references, all neighbors are hull
386  // simplices except for the 0th neighbor.
387  for (int i = 1; i < Traits::NDim + 1; i++) {
388  SimplexRef neighbor_ref = simplex.N[i];
389  Simplex& neighbor_simplex = storage[neighbor_ref];
390  if (!neighbor_simplex.marked[simplex::VISIBLE_HULL_WALK]) {
391  neighbor_simplex.marked[simplex::VISIBLE_HULL_WALK] = true;
392  marked_list.push_back(neighbor_ref);
393  SimplexRef interior_ref = neighbor_simplex.N[0];
394  Simplex& interior_simplex = storage[interior_ref];
395 
396  // find the index in the interior simplex vertex set of the vertex
397  // which is not part of the common facet
398  PointRef interior_peak_ref;
399  std::set_difference(
400  interior_simplex.V.begin(), interior_simplex.V.end(),
401  neighbor_simplex.V.begin(), neighbor_simplex.V.end(),
402  &interior_peak_ref);
403 
404  // compute the normal and offset of the facet with respect to this
405  // designated interior vertex
406  typedef Eigen::Matrix<Scalar, Traits::NDim, Traits::NDim> MatrixN;
407  typedef Eigen::Matrix<Scalar, Traits::NDim, 1> VectorN;
408  const Point& interior_point = storage[interior_peak_ref];
409  MatrixN A;
410  for (int i = 1; i < Traits::NDim + 1; i++) {
411  A.row(i - 1) = storage[neighbor_simplex.V[i]] - interior_point;
412  }
413  VectorN b;
414  b.fill(1.0);
415  VectorN normal = A.fullPivLu().solve(b).normalized();
416  Scalar offset = normal.dot(A.row(0));
417 
418  // orient the plane so that it "points away" from the interior or
419  // the point set
420  if (offset < 0) {
421  normal = -normal;
422  offset = -offset;
423  }
424 
425  // now test the query point for visibility
426  if (normal.dot(x_q - interior_point) > offset) {
427  visible_list.push_back(neighbor_ref);
428  }
429  }
430  }
431  }
432 
433  // clear all the marks
434  for (SimplexRef s_ref : marked_list) {
435  storage[s_ref].marked[simplex::VISIBLE_HULL_WALK] = false;
436  }
437 
438  // output results
439  for (SimplexRef s_ref : visible_list) {
440  *out++ = s_ref;
441  }
442 }
443 
444 template <class Traits, class OutputIterator>
445 void BreadthFirstSearch(typename Traits::Storage& storage,
446  typename Traits::SimplexRef simplex_ref,
447  OutputIterator out) {
448  typedef typename Traits::Simplex Simplex;
449  typedef typename Traits::SimplexRef SimplexRef;
450 
451  std::list<SimplexRef> bfs_queue;
452  bfs_queue.push_back(simplex_ref);
453  storage[simplex_ref].marked[simplex::BFS_QUEUED] = true;
454 
455  typename std::list<SimplexRef>::iterator iter = bfs_queue.begin();
456  for (; iter != bfs_queue.end(); ++iter) {
457  SimplexRef simplex_ref = *iter;
458  *out++ = simplex_ref;
459  Simplex& simplex = storage[simplex_ref];
460  for (SimplexRef neighbor_ref : simplex.N) {
461  Simplex& neighbor = storage[neighbor_ref];
462  if (!neighbor.marked[simplex::BFS_QUEUED]) {
463  neighbor.marked[simplex::BFS_QUEUED] = true;
464  bfs_queue.push_back(neighbor_ref);
465  }
466  }
467  }
468 
469  for (SimplexRef simplex_ref : bfs_queue) {
470  storage[simplex_ref].marked[simplex::BFS_QUEUED] = false;
471  }
472 }
473 
474 } // namespace edelsbrunner96
475 
476 #endif // EDELSBRUNNER96_LINE_WALKER_HPP_
void FeatureWalk(typename Traits::Storage &storage, typename Traits::SimplexRef s_0, InputIterator Vf_begin, InputIterator Vf_end, OutputIterator out)
Given a sub-simplex feature and a simplex adjacent to that feature, enumerate all simplices that are ...
simplex has been touched during a fuzzy walk
Definition: simplex.h:57
bool IsVisible(typename Traits::Storage &storage, typename Traits::SimplexRef s_ref, const typename Traits::Point &x_q, typename Traits::Scalar epsilon=0.0)
Given a hull simplex, return true if it is visible by the query point.
has been queud in breadth-first search
Definition: simplex.h:51
simplex has been touched during feature walk
Definition: simplex.h:53
void FuzzyWalk_(typename Traits::Storage &storage, const typename Traits::SimplexRef s_0, const typename Traits::Point &x_q, const typename Traits::Scalar epsilon, std::list< typename Traits::SimplexRef > &search_queue, OutputIterator out)
Implementation, exposed so that the search_queue can be examined in tests.
Traits::SimplexRef LineWalk(typename Traits::Storage &storage, typename Traits::SimplexRef s_0, const typename Traits::Point &p)
Starting at the median point of simplex s_0, walk the triangulation in the direction of p until the s...
Definition: line_walker.hpp:42
simplex has been touched during hull walk
Definition: simplex.h:55
void GetVisibleHull(typename Traits::Storage &storage, typename Traits::SimplexRef s_0, const typename Traits::Point &x_q, OutputIterator out)
Starting at some x-visible hull simplex, return a list of all x-visible hull simplices.
uint_t sort(KeyType *d_DstKey, ValueType *d_DstVal, KeyType *d_SrcKey, ValueType *d_SrcVal, uint_t arrayLength, uint_t sharedLength, Direction dir, uint_t globalThread)
kernel launcher, sorts an array of key/value pairs using the bitonic sort algorithm ...
Definition: kernels.cu.hpp:870
__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 FuzzyWalk(typename Traits::Storage &storage, const typename Traits::SimplexRef s_0, const typename Traits::Point &x_q, const typename Traits::Scalar epsilon, OutputIterator out)
Given a simplex in a triangulation and a query point, walk the triangulation in the direction of x_q ...
void BreadthFirstSearch(typename Traits::Storage &storage, typename Traits::SimplexRef simplex_ref, OutputIterator out)
Get a list of all simplices by breadth first search starting at simplex_ref.
simplex has been touched during line walk
Definition: simplex.h:54