Fréchet View  1.6.0
A Tool for Exploring Fréchet Distance Algorithms
shortest_paths.cpp
Go to the documentation of this file.
1 
2 #include <shortest_paths.h>
3 #include <exception>
4 #include <fs_path.h>
5 
6 using namespace frechet;
7 using namespace poly;
8 using namespace reach;
9 using namespace data;
10 
13 
14 
16  : tri(atri), funnel(atri.size())
17 { }
18 
20  : PolygonShortestPaths(atri),
21  source(nullptr), d(), epsilon(NAN), validPlacements(nullptr)
22 { }
23 
25  Vertex_handle start_point,
26  const frechet::reach::Placement* asource,
27  QLineF d, double epsilon,
28  frechet::reach::Graph::ptr validPlacements)
29 {
30  this->source = asource;
31  this->d = d;
32  this->epsilon = epsilon;
33  this->validPlacements = validPlacements;
34 
36 
37  this->source = nullptr;
38  this->validPlacements.reset();
39 }
40 
42 {
43  int size_before = tri.size();
44 
46  pl2.fs.lower()=pl2.fs.upper()=pl2.w = p2;
47 
50 
52 
53  Curve result;
54  for(Triangulation::Vertex* v=&*v2; v; v=v->prev)
55  result.prepend(*v);
56 
57  tri.resize(size_before);
58  return result;
59 }
60 
67 {
68  // reset all prev pointers
70  funnel.reset(start_point);
71 
72  Face_circulator f0 (start_point,start_point->face());
73  Face_circulator f = f0;
74  do {
75  Q_ASSERT(f!=Face_handle());
76  //std::cout << *f << std::endl;
77  if (tri.is_inner_face(f))
78  search(start_point,f);
79  } while(++f != f0);
80 
81  funnel.undo();
82  Q_ASSERT(funnel.empty());
83 }
84 
86 {
87  if (v->prev==&*prev)
88  return false; // already set
89 
90  Q_ASSERT(v->prev==nullptr);
91  v->prev = &*prev;
92  return true;
93 }
94 
95 //int iteration=0;
96 
98 {
100  return false;
101 
102  Q_ASSERT(this->source!=nullptr);
103  Q_ASSERT(v->prev);
104 
110  bool unreachabe = v->prev->prev && !v->prev->LR && !v->prev->BR;
111 // if (unreachabe) return;
112 
113  /* Update Free-Space Intervals */
114  // TODO float128
115  frechet::FreeSpace::calculateFreeSpaceSegment<accurate_float>(d.p1(), *v->prev, *v, epsilon, v->L);
116  frechet::FreeSpace::calculateFreeSpaceSegment<accurate_float>(d.p2(), *v->prev, *v, epsilon, v->R);
117  frechet::FreeSpace::calculateFreeSpaceSegment<accurate_float>(*v->prev, d.p1(), d.p2(), epsilon, v->B);
119  &v->L, &v->B,
120  v->prev->prev ? &v->prev->L : nullptr, nullptr);
122  &v->R, nullptr,
123  v->prev->prev ? &v->prev->R : nullptr, &v->B);
124 
125  /* Update Reachability */
126  if (v->prev->prev==nullptr)
127  initReachability(v);
128  else
130 
131  bool reachable=false;
132  if (v->target) {
133  Q_ASSERT(v->target->fs.lower() >= 0.0);
134  Q_ASSERT(v->target->fs.upper() >= v->target->fs.lower());
135  // This is a Placement target point
136  // Point is reachable -> this is a 'valid' placement
137  Interval T, TR, TR2;
138  FreeSpace::calculateFreeSpaceSegment<accurate_float>(*v, d.p1(), d.p2(), epsilon, T);
139  FreeSpace::fixBorderCase(nullptr,&T,&v->L,nullptr);
140  FreeSpace::fixBorderCase(nullptr,nullptr,&v->R,&T);
141 
142  if (v->LR || !T)
143  TR = T;
144  else if (v->BR)
145  TR = frechet::reach::FSPath::opposite(v->BR,T);
146 /* TODO mirrored reachability
147  if (v->RR2 || !T)
148  TR2 = T;
149  else if (v->BR2)
150  TR2 = frechet::reach::FSPath::opposite_min(v->BR2,T);
151 */
152  //ASSERT_EQ(TR.contains(1.0), v->RR.contains(1.0));
153  // There is one border case, where
154  // v->RR = empty, TR = [x..1]
155  // So 1.0 is only reachable through 1 single point.
156 
157  reachable = v->RR.contains(1.0) || TR.contains(1.0);
158 // TODO reachable2 = v->LR2.contains(1.0) || TR2.contains(0.0);
159  }
160 
161  if (reachable) {
162  validPlacements->add_range(source->gn, v->target->gn);
163  // add_valid_range(source->gn, v->target->gn);
164 // Q_ASSERT(validPlacements->contains_edge(source->gn, v->target->gn));
165  // debug hook:
166  Q_ASSERT(!after_placement_added || (after_placement_added(this,source,v->target),true));
167  }
168 /* TODO
169  if (reachable2) {
170  validPlacements2->add_range(flipped(source->gn) ??, v->target->gn);
171  }
172 */
173  // debug hook:
174  Q_ASSERT(!after_target_found || (after_target_found(this,v,reachable),true));
175  return true;
176 }
177 
179 {
180  // assumes v->L,B,R is calculated
181  // calculate reachable intervals starting at (0,0)
182  bool lr = v->L.contains(0.0);
183  bool br = v->B.contains(0.0);
184 
185  Q_ASSERT(lr == br);
186  if (lr) {
187  Q_ASSERT(br);
188  v->LR = v->L;
189  v->BR = v->B;
190  v->RR = v->R;
191  }
192  else {
193  Q_ASSERT(!br && !lr);
194  v->LR.clear();
195  v->BR.clear();
196  v->RR.clear();
197  }
198 
199  // TODO mirrorerd reachability
200 /*
201  bool rr = v->R.contains(0.0);
202  br = v->B.contains(1.0);
203 
204  Q_ASSERT(rr==br);
205  if (rr) {
206  Q_ASSERT(br);
207  v->LR2 = v->L;
208  v->BR2 = v->B;
209  v->RR2 = v->R;
210  }
211  else {
212  v->LR2.clear();
213  v->BR2.clear();
214  v->RR2.clear();
215  }
216 */
217 }
218 
224 {
225  if (v->prev->LR.contains(1.0) && v->L.contains(0.0))
226  v->LR = v->L;
227  else
228  v->LR.clear();
229 
230  if (v->prev->LR)
231  v->BR = v->B;
232  else if (v->prev->BR && v->B)
233  v->BR = frechet::reach::FSPath::opposite(v->prev->BR,v->B);
234  else
235  v->BR.clear();
236 
237  if (v->BR || (v->prev->RR.contains(1.0) && v->R.contains(0.0)))
238  v->RR = v->R;
239  else if (v->LR && v->R)
240  v->RR = frechet::reach::FSPath::opposite(v->LR,v->R);
241  else
242  v->RR.clear();
243 
244  // TODO mirrored reachability
245 /*
246  if (v->prev->RR2.contains(1.0) && v->R.contains(0.0))
247  v->RR2 = v->R;
248  else
249  v->RR2.clear();
250 
251  if (v->prev->RR2)
252  v->BR2 = v->B;
253  else if (v->prev->BR2 && v->B)
254  v->BR2 = frechet::reach::FSPath::opposite_min(v->prev->BR2,v->B); // TODO flipping required??
255  else
256  v->BR2.clear();
257 
258  if (v->BR2 || (v->prev->LR2.contains(1.0) && v->L.contains(0.0))) // TODO ??
259  v->LR2 = v->L;
260  else if (v->LR2 && v->R) // TODO ??
261  v->LR2 = frechet::reach::FSPath::opposite(v->LR2,v->R);
262  else
263  v->LR2.clear();
264 */
265 }
266 
268 {
269  int i = f->index(start_point);
270  Edge e(f,i);
271 
272  Q_ASSERT(funnel.apex()==start_point);
273  Vertex_handle v1 = f->vertex(f->ccw(i));
274  Vertex_handle v2 = f->vertex(f->cw(i));
275 
276  updateSPSegment(funnel.apex(), v1);
277  updateSPSegment(funnel.apex(), v2);
278 
279  if (! tri.is_polygon_edge(e))
280  {
281  funnel.addLeft(v1);
282  funnel.addRight(v2);
283  search(e);
284  funnel.undo();
285  funnel.undo();
286  }
287 }
288 
290 {
291  for(int i=-funnel.leftSize(); i <= funnel.rightSize(); ++i) {
292  if (i==0) std::cout << " apex=";
293  //std::cout << funnel[i]->index << " ";
294  }
295  //std::cout << std::endl;
296 }
297 
299 {
300  // candidate point is opposite e
302  int t = findTangent(v);
303 
304  updateSPSegment(funnel[t], v);
305 
306  // next Diagonals
307  Face_handle f1 = e.first;
308  int i = e.second;
309 
310  Face_handle f2 = f1->neighbor(i);
311  Edge d1 (f2,f2->index(funnel.leftEnd()));
312  Edge d2 (f2,f2->index(funnel.rightEnd()));
313 
314  if (false/*v->index>=643*/) {
315  //std::cout << v->index << "," << v->prev->index << ", " << tri.segment(e) << std::endl;
317  //std::cout << f2 << std::endl;
318  //std::cout << tri.segment(d1) << std::endl;
319  //std::cout << tri.segment(d2) << std::endl;
320  }
321  Q_ASSERT(tri.segment(d1)==Segment(funnel.rightEnd()->pindex,v->pindex));
322  Q_ASSERT(tri.segment(d2)==Segment(funnel.leftEnd()->pindex,v->pindex));
323 
324  if (!tri.is_polygon_edge(d1))
325  { // Right sub-funnel
327  funnel.addLeft(v);
328  search(d1);
329  funnel.undo();
330  funnel.undo();
331  }
332 
333  if (!tri.is_polygon_edge(d2))
334  { // Left sub-funnel
336  funnel.addRight(v);
337  search(d2);
338  funnel.undo();
339  funnel.undo();
340  }
341 }
342 
344 {
345  Q_ASSERT(t!=0);
346  Q_ASSERT(t >= -funnel.leftSize());
347  Q_ASSERT(t <= funnel.rightSize());
348 
349  if (t > 0) {
350  // right funnel: v is on the LEFT of [ab]
351  Vertex_handle a = funnel[t-1];
352  Vertex_handle b = funnel[t];
353  int hpt = halfplaneTest<accurate_float>(a,b,v);
354  return hpt < 0;
359  }
360  else {
361  Q_ASSERT(t < 0);
362  Vertex_handle a = funnel[t+1];
363  Vertex_handle b = funnel[t];
364  int hpt = halfplaneTest<accurate_float>(a,b,v);
365  return hpt > 0;
366  }
367 }
368 
371 {
372  Q_ASSERT((assertFunnel(),true));
373  // we expect a consistent, non-trivial funnel
374 
375  // dovetailed doubling search from left and right
376  int left = funnel.leftSize();
377  int right = funnel.rightSize();
378 
379  if (left>0 && isTangent(-left,v))
380  return -left;
381  if (right>0 && isTangent(right,v))
382  return right;
383 
384  for(int incr = 1; (incr < left) || (incr < right); incr*=2)
385  {
386  if ((incr < left) && isTangent(-left+incr,v))
387  return findTangentLeft(-left,-left+incr,v);
388  // TODO findTangentLeft(-left+incr/2,-left+incr,v);
389 
390  if ((incr < right) && isTangent(right-incr,v))
391  return findTangentRight(right-incr,right,v);
392  // TODO findTangentRight(right-incr,right-incr/2,v);
393  }
394  if (left>0 && isTangent(-1,v))
395  return findTangentLeft(-left,-1,v);
396  if (right>0 && isTangent(+1,v))
397  return findTangentRight(+1,right,v);
398 
399  // if there is no tangent, split at apex:
400  return 0;
401 }
402 
404 {
405  // perform binary search
406  // for max. tangent
407  Q_ASSERT(t1 < 0);
408  Q_ASSERT(t2 < 0);
409  Q_ASSERT(t1 < t2);
410  Q_ASSERT(!isTangent(t1,v));
411  Q_ASSERT( isTangent(t2,v));
412 
413  while ((t1+1) < t2) {
414  int t3 = (t1+t2)/2;
415  if (isTangent(t3,v))
416  t2=t3;
417  else
418  t1=t3;
419  }
420  return t2;
421 }
422 
424 {
425  // perform binary search
426  // for max. tangent
427  Q_ASSERT(t1 > 0);
428  Q_ASSERT(t2 > 0);
429  Q_ASSERT(t1 < t2);
430  Q_ASSERT( isTangent(t1,v));
431  Q_ASSERT(!isTangent(t2,v));
432 
433  while ((t1+1) < t2) {
434  int t3 = (t1+t2)/2;
435  if (isTangent(t3,v))
436  t1=t3;
437  else
438  t2=t3;
439  }
440  return t1;
441 }
442 
446 {
447  Q_ASSERT(funnel.size() >= 1);
448 
449  if (funnel.rightSize() >= 1) {
450  Q_ASSERT(funnel[1]->prev == &*funnel[0]);
451  }
452  for(int t=2; t <= funnel.rightSize(); ++t)
453  {
454  Q_ASSERT(funnel[t]->prev == &*funnel[t-1]);
455  Q_ASSERT(funnel[t-1]->prev == &*funnel[t-2]);
456  Q_ASSERT(isTangent(t-1, funnel[t]));
457  }
458  if (funnel.leftSize() >= 1) {
459  if(funnel[-1]->prev != &*funnel[0])
460  throw std::exception();
461  Q_ASSERT(funnel[-1]->prev == &*funnel[0]);
462  }
463  for(int t=2; t <= funnel.leftSize(); ++t)
464  {
465  Q_ASSERT(funnel[-t]->prev == &*funnel[-t+1]);
466  Q_ASSERT(funnel[-t+1]->prev == &*funnel[-t+2]);
467  Q_ASSERT(isTangent(-t+1,funnel[-t]));
468  }
469 }
Represents a polygon line segment from node i to j.
Definition: types.h:39
Triangulation & tri
the Triangulation
Triangulation::Face_handle Face_handle
static assert_hook1 after_target_found
unit test and debugging hook function; called when a target vertex is found
Triangulation::Vertex_handle Vertex_handle
Vertex_handle addTargetVertex(const frechet::reach::Placement *p)
add a vertex for computing valid placements
static void fixBorderCase(data::Interval *N, data::Interval *E, data::Interval *S, data::Interval *W)
fix border case at the joint of four intervals
Definition: freespace.cpp:252
int findTangentRight(int t1, int t2, Vertex_handle v) const
find a tangent from a point to the right part of the funnel
Triangulation::Face_circulator Face_circulator
global definitions for all algorithms.
virtual bool updateSPSegment(Vertex_handle prev, Vertex_handle v) override
hook function that is called by the shortest-tree search whenever a new segment is added to the tree....
static Segment segment(Edge e)
void(* assert_hook1)(PolygonShortestPathsFS *, Vertex_handle v, bool reachable)
static bool is_inner_face(Face_handle f)
double epsilon
free-space parameter epsilon
static bool is_polygon_edge(Edge e)
void findShortestPaths(Vertex_handle start_point)
traverse the polygon and report found paths
void undo(const Frame &fr)
undo an operation
const frechet::reach::Placement * source
source placement and
void resize(int sz)
reset number of vertexes in triangulation
Vertex_handle addVertex(double w)
add a triangulation vertex on the edge of the polygon (does not correspond to a polygon vertex)
void reset(const T &apex)
reset the qeueue with exactly one element
double upper() const
Definition: interval.h:96
static Interval opposite(const Interval &LR, const Interval &RF)
Definition: fs_path.cpp:398
void propagateReachability(Vertex_handle v)
update free-space and reachability info, called when a vertex is updated in the shortest-paths tree
void search(Vertex_handle start_point, Face_handle f)
compute the shortest-paths tree
bool isTangent(int t, Vertex_handle v) const
tangent test
PolygonShortestPathsFS(Triangulation &atri)
default constructor
Curve find1ShortestPath(double p1, double p2)
compute the shortest path between two points on the boundary of the polygon
void findShortestPaths(Vertex_handle start_point, const frechet::reach::Placement *asource, QLineF d, double epsilon, frechet::reach::Graph::ptr validPlacements)
void addLeft(const T &t)
push a new element to the left side of the queue
static assert_hook2 after_placement_added
unit test and debugging hook function; called when a valid placement is updated
int findTangentLeft(int t1, int t2, Vertex_handle v) const
find a tangent from a point to the left part of the funnel
void removeRightUpto(int t)
pop entries from the right end; Adjust the right end pointer and the apex, as necessary.
base class for vertexes in a Triangulation Data Structure.
Definition: triangulation.h:40
void assertFunnel() const
assertion for unit testing: is the Funnel in a consistent state?
void removeLeftUpto(int t)
pop entries from the left end; Adjust the left end pointer and the apex, as necessary.
QPolygonF Curve
a polygonal curve in the plane; with double floating point precision. This type is heavily used throu...
Definition: types.h:20
double lower() const
Definition: interval.h:92
boost::shared_ptr< Graph > ptr
Definition: graph_boost.h:59
void(* assert_hook2)(PolygonShortestPathsFS *, const frechet::reach::Placement *, const frechet::reach::Placement *)
void initReachability(Vertex_handle v)
set up reachability info for a triangulation vertex
placement of a diagonal point in free-space diagram When calculating valid placements in [buchin06]
Definition: graph_model.h:132
Vertex_handle oppositeVertex(Edge e) const
the second vertex opposite to an edge. Keep in mind that Edge object are desribed as a Face + opposit...
compute Shortest-Paths-Tree on a polygon
PolygonShortestPaths(Triangulation &atri)
default constructor
void printFunnel(const PolygonShortestPaths::Funnel &funnel)
void addRight(const T &t)
push a new element to the right side of the queue
virtual bool updateSPSegment(Vertex_handle prev, Vertex_handle v)
called whenever a ne segment is added to the shortest path tree; hook for derived classes
Interval fs
Free-Space Interval.
Definition: graph_model.h:135
Wrapper for CGAL::Triangulation_Data_Structure https://doc.cgal.org/latest/TDS_2/index....
TDS::Vertex_handle Vertex_handle
handle to a vertex (handles are managed by TDS base class)
int findTangent(Vertex_handle v) const
find a tangent from a point to the funnel
an interval of two double values.
Definition: interval.h:31
void clearPredecessors()
used during shortest-paths computation: reset shortest-path tree information on all vertexes
frechet::reach::Graph::ptr validPlacements
result: holds the valid placements on return
IndexRange gn
mapping to nodes of the reachability graph (see GraphModel)
Definition: graph_model.h:140