All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Voronoi.cxx
Go to the documentation of this file.
1 /**
2  * @file VoronoiDiagram.cxx
3  *
4  * @brief Producer module to create 3D clusters from input hits
5  *
6  */
7 
8 // std includes
9 #include <iostream>
10 #include <numeric>
11 
12 // Framework Includes
16 
17 // LArSoft includes
18 
19 // boost includes
20 #include <boost/polygon/voronoi.hpp>
21 
22 // Declare this here for boost
23 namespace boost
24 {
25  namespace polygon
26  {
27  template <>
28  struct geometry_concept<dcel2d::Point>
29  {
30  typedef point_concept type;
31  };
32 
33  template <>
34  struct point_traits<dcel2d::Point>
35  {
36  typedef int coordinate_type;
37 
38  static inline coordinate_type get(const dcel2d::Point& point, orientation_2d orient)
39  {
40  return (orient == HORIZONTAL) ? std::get<1>(point) : std::get<0>(point);
41  }
42  };
43  }
44 }
45 
46 //------------------------------------------------------------------------------------------------------------------------------------------
47 // implementation follows
48 
49 namespace voronoi2d {
50 
52  fHalfEdgeList(halfEdgeList),
53  fVertexList(vertexList),
54  fFaceList(faceList),
55  fXMin(0.),
56  fXMax(0.),
57  fYMin(0.),
58  fYMax(0.),
59  fVoronoiDiagramArea(0.)
60 {
61  fHalfEdgeList.clear();
62  fVertexList.clear();
63  fFaceList.clear();
64  fPointList.clear();
65  fSiteEventList.clear();
66  fCircleEventList.clear();
67  fCircleNodeList.clear();
68  fConvexHullList.clear();
69 
70  // And the area
72 }
73 
74 //------------------------------------------------------------------------------------------------------------------------------------------
75 
77 {
78 }
79 
80 //------------------------------------------------------------------------------------------------------------------------------------------
81 
82 bool VoronoiDiagram::isLeft(const dcel2d::Point& p0, const dcel2d::Point& p1, const dcel2d::Point& pCheck) const
83 {
84  // Use the cross product to determine if the check point lies to the left, on or right
85  // of the line defined by points p0 and p1
86  return crossProduct(p0, p1, pCheck) > 0;
87 }
88 
89 //------------------------------------------------------------------------------------------------------------------------------------------
90 
91 double VoronoiDiagram::crossProduct(const dcel2d::Point& p0, const dcel2d::Point& p1, const dcel2d::Point& p2) const
92 {
93  // Define a quick 2D cross product here since it will used quite a bit!
94  double deltaX = std::get<0>(p1) - std::get<0>(p0);
95  double deltaY = std::get<1>(p1) - std::get<1>(p0);
96  double dCheckX = std::get<0>(p2) - std::get<0>(p0);
97  double dCheckY = std::get<1>(p2) - std::get<1>(p0);
98 
99  return ((deltaX * dCheckY) - (deltaY * dCheckX));
100 }
101 
102 //------------------------------------------------------------------------------------------------------------------------------------------
103 
104 double VoronoiDiagram::Area() const
105 {
106  double area(0.);
107 
108  // Compute the area by taking advantage of
109  // 1) the ability to decompose a convex hull into triangles,
110  // 2) the ability to use the cross product to calculate the area
111  // So, the technique is to pick a point (for technical reasons we use 0,0)
112  // and then sum the signed area of triangles formed from this point to two adjecent
113  // vertices on the convex hull.
114  dcel2d::Point center(0.,0.,NULL);
115  dcel2d::Point lastPoint = fPointList.front();
116 
117  for(const auto& point : fPointList)
118  {
119  if (point != lastPoint) area += 0.5 * crossProduct(center,lastPoint,point);
120 
121  lastPoint = point;
122  }
123 
124  return area;
125 }
126 
127 //------------------------------------------------------------------------------------------------------------------------------------------
128 
129 bool compareSiteEventPtrs(const IEvent* left, const IEvent* right) {return *left < *right;}
130 
131 //------------------------------------------------------------------------------------------------------------------------------------------
132 
134 {
135  // Insure all the local data structures have been cleared
136  fHalfEdgeList.clear();
137  fVertexList.clear();
138  fFaceList.clear();
139  fPointList.clear();
140  fSiteEventList.clear();
141  fCircleEventList.clear();
142  fCircleNodeList.clear();
143  fNumBadCircles = 0;
144 
145  std::cout << "******************************************************************************************************************" << std::endl;
146  std::cout << "******************************************************************************************************************" << std::endl;
147  std::cout << "******************************************************************************************************************" << std::endl;
148  std::cout << "==> # input points: " << pointList.size() << std::endl;
149 
150  // Define the priority queue to contain our events
151  EventQueue eventQueue(compareSiteEventPtrs);
152 
153  // Now populate the event queue with site events
154  for(const auto& point : pointList)
155  {
156  fSiteEventList.emplace_back(point);
157  IEvent* iEvent = &fSiteEventList.back();
158  eventQueue.push(iEvent);
159  }
160 
161  // Declare the beachline which will contain the BSTNode objects for site events
162  BeachLine beachLine;
163 
164  // Finally, we need a container for our circle event BSTNodes
165  BSTNodeList circleNodeList;
166 
167  // Now process the queue
168  while(!eventQueue.empty())
169  {
170  IEvent* event = eventQueue.top();
171 
172  eventQueue.pop();
173 
174  // If a site or circle event then handle appropriately
175  if (event->isSite()) handleSiteEvents(beachLine, eventQueue, event);
176  else if (event->isValid()) handleCircleEvents(beachLine, eventQueue, event);
177  }
178 
179  std::cout << "*******> # input points: " << pointList.size() << ", remaining leaves: " << beachLine.countLeaves() << ", " << beachLine.traverseBeach() << ", # bad circles: " << fNumBadCircles << std::endl;
180  std::cout << " Faces: " << fFaceList.size() << ", Vertices: " << fVertexList.size() << ", # half edges: " << fHalfEdgeList.size() << std::endl;
181 
182  // Get the bounding box
184 
185  std::cout << " Range min/maxes, x: " << fXMin << ", " << fXMax << ", y: " << fYMin << ", " << fYMax << std::endl;
186 
187  // Terminate the infinite edges
188  terminateInfiniteEdges(beachLine, std::get<0>(pointList.front()));
189 
190  // Look for open faces
191  int nOpenFaces(0);
192 
193  std::map<int,int> edgeCountMap;
194  std::map<int,int> openCountMap;
195 
196  for (const auto& face : fFaceList)
197  {
198  int nEdges(1);
199  bool closed(false);
200  const dcel2d::HalfEdge* startEdge = face.getHalfEdge();
201 
202  // Count forwards
203  for(const dcel2d::HalfEdge* halfEdge = startEdge->getNextHalfEdge(); halfEdge && !closed;)
204  {
205  if (halfEdge->getFace() != &face)
206  {
207  std::cout << " ===> halfEdge does not match face: " << halfEdge << ", face: " << halfEdge->getFace() << ", base: " << &face << std::endl;
208  }
209 
210  if (halfEdge == startEdge)
211  {
212  closed = true;
213  break;
214  }
215  nEdges++;
216  halfEdge = halfEdge->getNextHalfEdge();
217  }
218 
219  // Count backwards. Note if face is closed then nothing to do here
220  if (!closed)
221  {
222  for(const dcel2d::HalfEdge* halfEdge = startEdge->getLastHalfEdge(); halfEdge && !closed;)
223  {
224  if (halfEdge->getFace() != &face)
225  {
226  std::cout << " ===> halfEdge does not match face: " << halfEdge << ", face: " << halfEdge->getFace() << ", base: " << &face << std::endl;
227  }
228 
229  if (halfEdge == startEdge)
230  {
231  closed = true;
232  break;
233  }
234  nEdges++;
235  halfEdge = halfEdge->getLastHalfEdge();
236  }
237  }
238 
239  if (!closed)
240  {
241  nOpenFaces++;
242  openCountMap[nEdges]++;
243  }
244 
245  edgeCountMap[nEdges]++;
246  }
247 
248  std::cout << "==> Found " << nOpenFaces << " open faces from total of " << fFaceList.size() << std::endl;
249  for(const auto& edgeCount : edgeCountMap) std::cout << " -> all edges, # edges: " << edgeCount.first << ", count: " << edgeCount.second << std::endl;
250  for(const auto& edgeCount : openCountMap) std::cout << " -> open edges, # edges: " << edgeCount.first << ", count: " << edgeCount.second << std::endl;
251  std::cout << "******************************************************************************************************************" << std::endl;
252  std::cout << "******************************************************************************************************************" << std::endl;
253  std::cout << "******************************************************************************************************************" << std::endl;
254 
255  // Clear internal containers that are no longer useful
256  fSiteEventList.clear();
257  fCircleEventList.clear();
258  fCircleNodeList.clear();
259 
260  return;
261 }
262 
263 //------------------------------------------------------------------------------------------------------------------------------------------
264 
266 {
267  // Insure all the local data structures have been cleared
268  fHalfEdgeList.clear();
269  fVertexList.clear();
270  fFaceList.clear();
271  fPointList.clear();
272  fSiteEventList.clear();
273  fCircleEventList.clear();
274  fCircleNodeList.clear();
275  fNumBadCircles = 0;
276 
277  std::cout << "******************************************************************************************************************" << std::endl;
278  std::cout << "******************************************************************************************************************" << std::endl;
279  std::cout << "******************************************************************************************************************" << std::endl;
280  std::cout << "==> # input points: " << pointList.size() << std::endl;
281 
282  // Construct out voronoi diagram
283  boost::polygon::voronoi_diagram<double> vd;
284  boost::polygon::construct_voronoi(pointList.begin(),pointList.end(),&vd);
285 
286  // Make maps for translating from boost to me (or we can rewrite our code in boost... for now maps)
287  using BoostEdgeToEdgeMap = std::map<const boost::polygon::voronoi_edge<double>*, dcel2d::HalfEdge*>;
288  using BoostVertexToVertexMap = std::map<const boost::polygon::voronoi_vertex<double>*, dcel2d::Vertex*>;
289  using BoostCellToFaceMap = std::map<const boost::polygon::voronoi_cell<double>*, dcel2d::Face*>;
290 
291  BoostEdgeToEdgeMap boostEdgeToEdgeMap;
292  BoostVertexToVertexMap boostVertexToVertexMap;
293  BoostCellToFaceMap boostCellToFaceMap;
294 
295  // Loop over the edges
296  for(const auto& edge : vd.edges())
297  {
298  const boost::polygon::voronoi_edge<double>* twin = edge.twin();
299 
300  boostTranslation(pointList, &edge, twin, boostEdgeToEdgeMap, boostVertexToVertexMap, boostCellToFaceMap);
301  boostTranslation(pointList, twin, &edge, boostEdgeToEdgeMap, boostVertexToVertexMap, boostCellToFaceMap);
302  }
303 
304  //std::cout << "==> Found " << nOpenFaces << " open faces from total of " << fFaceList.size() << std::endl;
305  //for(const auto& edgeCount : edgeCountMap) std::cout << " -> all edges, # edges: " << edgeCount.first << ", count: " << edgeCount.second << std::endl;
306  //for(const auto& edgeCount : openCountMap) std::cout << " -> open edges, # edges: " << edgeCount.first << ", count: " << edgeCount.second << std::endl;
307  std::cout << "==> Boost returns " << fHalfEdgeList.size() << " edges, " << fFaceList.size() << " faces, " << fVertexList.size() << " vertices " << std::endl;
308  std::cout << " " << vd.edges().size() << " edges, " << vd.cells().size() << " faces, " << vd.vertices().size() << " vertices" << std::endl;
309  std::cout << "******************************************************************************************************************" << std::endl;
310  std::cout << "******************************************************************************************************************" << std::endl;
311  std::cout << "******************************************************************************************************************" << std::endl;
312 
313  // Clear internal containers that are no longer useful
314  fSiteEventList.clear();
315  fCircleEventList.clear();
316  fCircleNodeList.clear();
317 
318  return;
319 }
320 
322  const boost::polygon::voronoi_edge<double>* edge,
323  const boost::polygon::voronoi_edge<double>* twin,
324  BoostEdgeToEdgeMap& boostEdgeToEdgeMap,
325  BoostVertexToVertexMap& boostVertexToVertexMap,
326  BoostCellToFaceMap& boostCellToFaceMap)
327 {
328  dcel2d::HalfEdge* halfEdge = NULL;
329  dcel2d::HalfEdge* twinEdge = NULL;
330 
331  if (boostEdgeToEdgeMap.find(edge) != boostEdgeToEdgeMap.end()) halfEdge = boostEdgeToEdgeMap.at(edge);
332  else
333  {
334  fHalfEdgeList.emplace_back();
335 
336  halfEdge = &fHalfEdgeList.back();
337 
338  boostEdgeToEdgeMap[edge] = halfEdge;
339  }
340 
341  if (boostEdgeToEdgeMap.find(twin) != boostEdgeToEdgeMap.end()) twinEdge = boostEdgeToEdgeMap.at(twin);
342  else
343  {
344  fHalfEdgeList.emplace_back();
345 
346  twinEdge = &fHalfEdgeList.back();
347 
348  boostEdgeToEdgeMap[twin] = twinEdge;
349  }
350 
351  // Do the primary half edge first
352  const boost::polygon::voronoi_vertex<double>* boostVertex = edge->vertex1();
353  dcel2d::Vertex* vertex = NULL;
354 
355  // note we can have a null vertex (infinite edge)
356  if (boostVertex)
357  {
358  if (boostVertexToVertexMap.find(boostVertex) == boostVertexToVertexMap.end())
359  {
360  dcel2d::Coords coords(boostVertex->y(),boostVertex->x(),0.);
361 
362  fVertexList.emplace_back(coords, halfEdge);
363 
364  vertex = &fVertexList.back();
365 
366  boostVertexToVertexMap[boostVertex] = vertex;
367  }
368  else vertex = boostVertexToVertexMap.at(boostVertex);
369  }
370 
371  const boost::polygon::voronoi_cell<double>* boostCell = edge->cell();
372  dcel2d::Face* face = NULL;
373 
374  if (boostCellToFaceMap.find(boostCell) == boostCellToFaceMap.end())
375  {
376  dcel2d::PointList::const_iterator pointItr = pointList.begin();
377  int pointIdx = boostCell->source_index();
378 
379  std::advance(pointItr, pointIdx);
380 
381  const dcel2d::Point& point = *pointItr;
382  dcel2d::Coords coords(std::get<0>(point),std::get<1>(point),0.);
383 
384  fFaceList.emplace_back(halfEdge,coords,std::get<2>(point));
385 
386  face = &fFaceList.back();
387 
388  boostCellToFaceMap[boostCell] = face;
389  }
390 
391  halfEdge->setTargetVertex(vertex);
392  halfEdge->setFace(face);
393  halfEdge->setTwinHalfEdge(twinEdge);
394 
395  // For the prev/next half edges we can have two cases, so check:
396  if (boostEdgeToEdgeMap.find(edge->next()) != boostEdgeToEdgeMap.end())
397  {
398  dcel2d::HalfEdge* nextEdge = boostEdgeToEdgeMap.at(edge->next());
399 
400  halfEdge->setNextHalfEdge(nextEdge);
401  nextEdge->setLastHalfEdge(halfEdge);
402  }
403 
404  if (boostEdgeToEdgeMap.find(edge->prev()) != boostEdgeToEdgeMap.end())
405  {
406  dcel2d::HalfEdge* lastEdge = boostEdgeToEdgeMap.at(edge->prev());
407 
408  halfEdge->setLastHalfEdge(lastEdge);
409  lastEdge->setNextHalfEdge(halfEdge);
410  }
411 
412  return;
413 }
414 
416  EventQueue& eventQueue,
417  IEvent* siteEvent)
418 {
419  // Insert the new site event into the beach line and recover the leaf for the
420  // new arc in the beach line
421  // NOTE: invalidation of any possible circle events occurs in the call to this routine
422  BSTNode* newLeaf = beachLine.insertNewLeaf(siteEvent);
423 
424  // Create a new vertex for each site event
425  fFaceList.emplace_back(dcel2d::Face(NULL,siteEvent->getCoords(),std::get<2>(siteEvent->getPoint())));
426 
427  dcel2d::Face* face = &fFaceList.back();
428 
429  newLeaf->setFace(face);
430 
431  // If we are the first site added to the BST then we are done
432  if (!(newLeaf->getPredecessor() || newLeaf->getSuccessor())) return;
433 
434  // So, now we deal with creating the edges that will be mapped out by the two breakpoints as they
435  // move with the beachline. Note that this is a single edge pair (edge and its twin) between the
436  // two breakpoints that have been created.
437  fHalfEdgeList.push_back(dcel2d::HalfEdge());
438 
439  dcel2d::HalfEdge* halfEdge = &fHalfEdgeList.back();
440 
441  fHalfEdgeList.push_back(dcel2d::HalfEdge());
442 
443  dcel2d::HalfEdge* halfTwin = &fHalfEdgeList.back();
444 
445  // Each half edge is the twin of the other
446  halfEdge->setTwinHalfEdge(halfTwin);
447  halfTwin->setTwinHalfEdge(halfEdge);
448 
449  // Point the breakpoint to the new edge
450  newLeaf->getPredecessor()->setHalfEdge(halfEdge);
451  newLeaf->getSuccessor()->setHalfEdge(halfTwin);
452 
453  // The second half edge corresponds to our face for the left breakpoint...
454  face->setHalfEdge(halfTwin);
455  halfTwin->setFace(face);
456 
457  // Update for the left leaf
458  BSTNode* leftLeaf = newLeaf->getPredecessor()->getPredecessor();
459 
460  // This needs to be checked since the first face will not have an edge set to it
461  if (!leftLeaf->getFace()->getHalfEdge()) leftLeaf->getFace()->setHalfEdge(halfEdge);
462 
463  halfEdge->setFace(leftLeaf->getFace());
464 
465  // Try to make circle events either side of this leaf
466  makeLeftCircleEvent( eventQueue, newLeaf, siteEvent->xPos());
467  makeRightCircleEvent(eventQueue, newLeaf, siteEvent->xPos());
468 
469  return;
470 }
471 
473  EventQueue& eventQueue,
474  IEvent* circleEvent)
475 {
476  BSTNode* circleNode = circleEvent->getBSTNode();
477  BSTNode* arcNode = circleNode->getAssociated();
478 
479  // Recover the half edges for the breakpoints either side of the arc we're removing
480  dcel2d::HalfEdge* leftHalfEdge = arcNode->getPredecessor()->getHalfEdge();
481  dcel2d::HalfEdge* rightHalfEdge = arcNode->getSuccessor()->getHalfEdge();
482 
483  if (leftHalfEdge->getTwinHalfEdge()->getFace() != rightHalfEdge->getFace())
484  {
485  std::cout << ">>>> Face mismatch in circle handling, left face: " << leftHalfEdge->getFace() << ", left twin: " << leftHalfEdge->getTwinHalfEdge()->getFace() << ", right: " << rightHalfEdge->getFace() << ", right twin: " << rightHalfEdge->getTwinHalfEdge()->getFace() << ", arc face: " << arcNode->getFace() << std::endl;
486  }
487 
488  // Create the new vertex point
489  // Don't forget we need to swap coordinates when returning to the outside world
490  dcel2d::Coords vertexPos(circleEvent->circleCenter());
491 
492  fVertexList.push_back(dcel2d::Vertex(vertexPos,leftHalfEdge->getTwinHalfEdge()));
493 
494  dcel2d::Vertex* vertex = &fVertexList.back();
495 
496  // The edges we obtained "emanate" from the new vertex point,
497  // their twins will point to it
498  leftHalfEdge->getTwinHalfEdge()->setTargetVertex(vertex);
499  rightHalfEdge->getTwinHalfEdge()->setTargetVertex(vertex);
500 
501  // Remove the arc which will return the new breakpoint
502  // NOTE: invalidation of any possible circle events occurs in the call to this routine
503  BSTNode* newBreakPoint = beachLine.removeLeaf(arcNode);
504 
505  // Now create edges associated with the new break point
506  fHalfEdgeList.push_back(dcel2d::HalfEdge());
507 
508  dcel2d::HalfEdge* halfEdgeOne = &fHalfEdgeList.back();
509 
510  fHalfEdgeList.push_back(dcel2d::HalfEdge());
511 
512  dcel2d::HalfEdge* halfEdgeTwo = &fHalfEdgeList.back();
513 
514  // Associate the first edge with the new break point
515  newBreakPoint->setHalfEdge(halfEdgeTwo);
516 
517  // These are twins
518  halfEdgeOne->setTwinHalfEdge(halfEdgeTwo);
519  halfEdgeTwo->setTwinHalfEdge(halfEdgeOne);
520 
521  // The second points to the vertex we just made
522  halfEdgeTwo->setTargetVertex(vertex);
523  vertex->setHalfEdge(halfEdgeOne);
524 
525  // halfEdgeTwo points to the vertex, face to left, so pairs with
526  // the leftHalfEdge (leaving the vertex, face to left)
527  halfEdgeTwo->setNextHalfEdge(leftHalfEdge);
528  leftHalfEdge->setLastHalfEdge(halfEdgeTwo);
529  halfEdgeTwo->setFace(leftHalfEdge->getFace());
530 
531  // halfEdgeOne emanates from the vertex, face to left, so pairs with
532  // the twin of the rightHalfEdge (pointing to vertex, face to left)
533  halfEdgeOne->setLastHalfEdge(rightHalfEdge->getTwinHalfEdge());
534  rightHalfEdge->getTwinHalfEdge()->setNextHalfEdge(halfEdgeOne);
535  halfEdgeOne->setFace(rightHalfEdge->getTwinHalfEdge()->getFace());
536 
537  // Finally, the twin of the leftHalfEdge points to the vertex (face to left)
538  // and will pair with the rightHalfEdge emanating from the vertex
539  // In this case they should already be sharing the same face
540  rightHalfEdge->setLastHalfEdge(leftHalfEdge->getTwinHalfEdge());
541  leftHalfEdge->getTwinHalfEdge()->setNextHalfEdge(rightHalfEdge);
542 
543  // We'll try to make circle events with the remnant arcs so get the pointers now
544  // Note that we want the former left and right arcs to be the middle arcs in this case
545  BSTNode* leftArc = newBreakPoint->getPredecessor();
546  BSTNode* rightArc = newBreakPoint->getSuccessor();
547 
548  // Look for a new circle candidates
549  // In this case, we'd like the right arc to be the middle of the right circle,
550  // the left arc to be the middle of the left circle, hence the logic below
551  makeRightCircleEvent(eventQueue, leftArc, circleEvent->xPos());
552  makeLeftCircleEvent(eventQueue, rightArc, circleEvent->xPos());
553 
554  return;
555 }
556 
558  BSTNode* leaf,
559  double beachLine)
560 {
561 
562  // Check status of triplet of site events to the left of this new leaf
563  if (leaf->getPredecessor())
564  {
565  BSTNode* midLeaf = leaf->getPredecessor()->getPredecessor();
566 
567  if (midLeaf && midLeaf->getPredecessor())
568  {
569  BSTNode* edgeLeaf = midLeaf->getPredecessor()->getPredecessor();
570 
571  // edge leaves must be different
572  if (leaf->getEvent()->getPoint() == edgeLeaf->getEvent()->getPoint()) return;
573 
574  if (edgeLeaf)
575  {
576  IEvent* circleEvent = makeCircleEvent(edgeLeaf, midLeaf, leaf, beachLine);
577 
578  // Did we succeed in making a circle event?
579  if (circleEvent)
580  {
581  // Add to the circle node list
582  fCircleNodeList.emplace_back(circleEvent);
583 
584  BSTNode* circleNode = &fCircleNodeList.back();
585 
586  // If there was an associated circle event to this node, invalidate it
587  if (midLeaf->getAssociated())
588  {
589  midLeaf->getAssociated()->setAssociated(NULL);
590  midLeaf->getAssociated()->getEvent()->setInvalid();
591  }
592 
593  // Now reset to point at the new one
594  midLeaf->setAssociated(circleNode);
595  circleNode->setAssociated(midLeaf);
596 
597  eventQueue.push(circleEvent);
598  }
599  }
600  }
601  }
602 
603  return;
604 }
605 
607  BSTNode* leaf,
608  double beachLine)
609 {
610  // Check status of triplet of site events to the left of this new leaf
611  if (leaf->getSuccessor())
612  {
613  BSTNode* midLeaf = leaf->getSuccessor()->getSuccessor();
614 
615  if (midLeaf && midLeaf->getSuccessor())
616  {
617  BSTNode* edgeLeaf = midLeaf->getSuccessor()->getSuccessor();
618 
619  if (leaf->getEvent()->getPoint() == edgeLeaf->getEvent()->getPoint()) return;
620 
621  if (edgeLeaf)
622  {
623  IEvent* circleEvent = makeCircleEvent(leaf, midLeaf, edgeLeaf, beachLine);
624 
625  // Did we succeed in making a circle event?
626  if (circleEvent)
627  {
628  // Add to the circle node list
629  fCircleNodeList.emplace_back(circleEvent);
630 
631  BSTNode* circleNode = &fCircleNodeList.back();
632 
633  // If there was an associated circle event to this node, invalidate it
634  if (midLeaf->getAssociated())
635  {
636  midLeaf->getAssociated()->setAssociated(NULL);
637  midLeaf->getAssociated()->getEvent()->setInvalid();
638  }
639 
640  // Now reset to point at the new one
641  midLeaf->setAssociated(circleNode);
642  circleNode->setAssociated(midLeaf);
643 
644  eventQueue.push(circleEvent);
645  }
646  }
647  }
648  }
649 
650  return;
651 }
652 
653 IEvent* VoronoiDiagram::makeCircleEvent(BSTNode* arc1, BSTNode* arc2, BSTNode* arc3, double beachLinePos)
654 {
655  // It might be that we don't create a new circle
656  IEvent* circle = 0;
657 
658  // First step is to calculate the center and radius of the circle determined by the three input site events
659  dcel2d::Coords center;
660  double radius;
661  double deltaR;
662 
663  IEvent* p1 = arc1->getEvent();
664  IEvent* p2 = arc2->getEvent();
665  IEvent* p3 = arc3->getEvent();
666 
667  // Compute the center of the circle. Note that this will also automagically check that breakpoints are
668  // converging so that this is a circle we want
669  if (computeCircleCenter( p1->getCoords(), p2->getCoords(), p3->getCoords(), center, radius, deltaR))
670  {
671  double circleBottomX = center[0] - radius;
672 
673  // Now check if the bottom of this circle lies below the beach line
674  if (beachLinePos >= circleBottomX - 10. * deltaR)
675  {
676  // Making a circle event!
677  dcel2d::Point circleBottom(circleBottomX, center[1], NULL);
678 
679  fCircleEventList.emplace_back(circleBottom, center);
680 
681  circle = &fCircleEventList.back();
682  }
683  else if (circleBottomX - beachLinePos < 1.e-4)
684  std::cout << "==> Circle close, beachLine: " << beachLinePos << ", circleBottomX: " << circleBottomX << ", deltaR: " << deltaR << ", d: " << circleBottomX - beachLinePos << std::endl;
685  }
686 
687  return circle;
688 }
689 
691  const dcel2d::Coords& p2,
692  const dcel2d::Coords& p3,
693  dcel2d::Coords& center,
694  double& radius,
695  double& delta) const
696 {
697  // The method is to translate the three points to a system where the first point is at the origin. Then we
698  // are looking for a circle that passes through the origin and the two remaining (translated) points. In
699  // this mode the circle radius is the distance from the origin to the circle center which simplifies the
700  // calculation.
701  double xCoord = p1[0];
702  double yCoord = p1[1];
703 
704  double x2 = p2[0] - xCoord;
705  double y2 = p2[1] - yCoord;
706  double x3 = p3[0] - xCoord;
707  double y3 = p3[1] - yCoord;
708 
709  double det = x2 * y3 - x3 * y2;
710 
711  // Points are colinear so cannot make a circle
712  // Or, if negative then the midpoint is "left" of line between first and third points meaning the
713  // circle curvature is the "wrong" way for making a circle event
714  if (det <= 0.) // std::numeric_limits<double>::epsilon())
715  //if (!(std::abs(det) > 0.)) // std::numeric_limits<double>::epsilon())
716  {
717  if (det > -std::numeric_limits<double>::epsilon())
718  std::cout << " --->Circle failure, det: " << det << ", mid x: " << p2[0] << ", y: " << p2[1]
719  << ", l x: " << p1[0] << ", y: " << p1[1]
720  << ", r x: " << p3[0] << ", y: " << p3[1] << std::endl;
721 
722  return false;
723  }
724 
725  double p2sqr = x2 * x2 + y2 * y2;
726  double p3sqr = x3 * x3 + y3 * y3;
727 
728  double cxpr = 0.5 * (y3 * p2sqr - y2 * p3sqr) / det;
729  double cypr = 0.5 * (x2 * p3sqr - x3 * p2sqr) / det;
730 
731  radius = std::sqrt(cxpr * cxpr + cypr * cypr);
732  center[0] = cxpr + xCoord;
733  center[1] = cypr + yCoord;
734  center[2] = 0.;
735 
736  // So... roundoff error can cause degenerate circles to get missed
737  // We need to attack that problem by calculating the radius all possible
738  // ways and then take the largest...
739  dcel2d::Coords p1Rad = p1 - center;
740  dcel2d::Coords p2Rad = p2 - center;
741  dcel2d::Coords p3Rad = p3 - center;
742 
743  std::vector<float> radSqrVec;
744 
745  radSqrVec.push_back(p1Rad.norm());
746  radSqrVec.push_back(p2Rad.norm());
747  radSqrVec.push_back(p3Rad.norm());
748 
749  double maxRadius = *std::max_element(radSqrVec.begin(),radSqrVec.end());
750 
751  delta = std::max(5.e-7, maxRadius - radius);
752 
753  if (radius > 1000.)
754  {
755 // std::cout << " ***> Radius = " << radius << ", circ x,y: " << center[0] << ", " << center[1] << ", p1: " << xCoord << "," << yCoord << ", p2: " << p2[0] << "," << p2[1] << ", p3: " << p3[0] << "," << p3[1] << ", det: " << det << std::endl;
756  fNumBadCircles++;
757  }
758 
759  return true;
760 }
761 
763  const dcel2d::Coords& p2,
764  const dcel2d::Coords& p3,
765  dcel2d::Coords& center,
766  double& radius,
767  double& delta) const
768 {
769  // Compute the circle center as the intersection of the two perpendicular bisectors of rays between the points
770  double slope12 = (p2[1] - p1[1]) / (p2[0] - p1[0]);
771  double slope32 = (p3[1] - p2[1]) / (p3[0] - p2[0]);
772 
773  if (std::abs(slope12 - slope32) <= std::numeric_limits<double>::epsilon())
774  {
775  std::cout << " >>>> Matching slopes! points: (" << p1[0] << "," << p1[1] << "), ("<< p2[0] << "," << p2[1] << "), ("<< p3[0] << "," << p3[1] << ")" << std::endl;
776 
777  return false;
778  }
779 
780  center[0] = (slope12 * slope32 * (p3[1] - p1[1]) + slope12 * (p2[0] + p3[0]) - slope32 * (p1[0] + p2[0])) / (2. * (slope12 - slope32));
781  center[1] = 0.5 * (p1[1] + p2[1]) - (center[0] - 0.5 * (p1[0] + p2[0])) / slope12;
782  center[2] = 0.;
783  radius = std::sqrt(std::pow((p2[0] - center[0]),2) + std::pow((p2[1] - center[1]),2));
784 
785  if (radius > 100.)
786  {
787  std::cout << " ***> Rad2 = " << radius << ", circ x,y: " << center[0] << "," << center[1] << ", p1: " << p1[0] << "," << p1[1] << ", p2: " << p2[0] << "," << p2[1] << ", p3: " << p3[0] << ", " << p3[1] << std::endl;
788  }
789 
790  return true;
791 }
792 
794  const dcel2d::Coords& p2,
795  const dcel2d::Coords& p3,
796  dcel2d::Coords& center,
797  double& radius,
798  double& delta) const
799 {
800  // Yet another bisector method to calculate the circle center...
801  double temp = p2[0] * p2[0] + p2[1] * p2[1];
802  double p1p2 = (p1[0] * p1[0] + p1[1] * p1[1] - temp) / 2.0;
803  double p2p3 = (temp - p3[0] * p3[0] - p3[1] * p3[1]) / 2.0;
804  double det = (p1[0] - p2[0]) * (p2[1] - p3[1]) - (p2[0] - p3[0]) * (p1[1] - p2[1]);
805 
806  if (std::abs(det) < 10. * std::numeric_limits<double>::epsilon())
807  {
808  std::cout << " >>>> Determinant zero! points: (" << p1[0] << "," << p1[1] << "), ("<< p2[0] << "," << p2[1] << "), ("<< p3[0] << "," << p3[1] << ")" << std::endl;
809 
810  return false;
811  }
812 
813  det = 1. / det;
814 
815  center[0] = (p1p2 * (p2[1] - p3[1]) - p2p3 * (p1[1] - p2[1])) * det;
816  center[1] = (p2p3 * (p1[0] - p2[0]) - p1p2 * (p2[0] - p3[0])) * det;
817  center[2] = 0.;
818 
819  radius = std::sqrt(std::pow((p1[0] - center[0]),2) + std::pow((p1[1] - center[1]),2));
820 
821  if (radius > 100.)
822  {
823  std::cout << " ***> Rad3 = " << radius << ", circ x,y: " << center[0] << "," << center[1] << ", p1: " << p1[0] << "," << p1[1] << ", p2: " << p2[0] << "," << p2[1] << ", p3: " << p3[0] << ", " << p3[1] << std::endl;
824  }
825 
826  return true;
827 }
828 
829 void VoronoiDiagram::terminateInfiniteEdges(BeachLine& beachLine, double beachLinePos)
830 {
831  // Need to complete processing of the beachline, the remaning leaves represent the site points with "infinite"
832  // edges which we need to terminate at our bounding box.
833  // For now let's just do step one which is to find the convex hull
834  const BSTNode* node = beachLine.getTopNode();
835 
836  // Do the convex hull independently but before terminating edges
837  getConvexHull(node);
838 
839  if (node)
840  {
841  // Run down the left branches to find the left most leaf
842  while(node->getLeftChild()) node = node->getLeftChild();
843 
844  // Things to keep track of...
845  int nodeCount(0);
846  int nBadBreaks(0);
847  double lastBreakPoint = std::numeric_limits<double>::lowest();
848 
849  // Now we are going to traverse across the beach line and process the remaining leaves
850  // This will identify the infinite edges and find the convex hull
851  while(node)
852  {
853  std::cout << "--------------------------------------------------------------------------------------------" << std::endl;
854 
855  // Do we have a leaf?
856  if (!node->getLeftChild() && !node->getRightChild())
857  {
858  std::cout << "node " << nodeCount << " has leaf: " << node << ", face: " << node->getFace() << ", pos: " << node->getEvent()->getCoords()[0] << "/" << node->getEvent()->getCoords()[1] << std::endl;
859  }
860  // Otherwise it is a break point
861  else
862  {
863  RootsPair roots;
864  double breakPoint = fUtilities.computeBreak(beachLinePos, node->getPredecessor()->getEvent(), node->getSuccessor()->getEvent(), roots);
865  double leftArcVal = fUtilities.computeArcVal(beachLinePos, breakPoint, node->getPredecessor()->getEvent());
866  double rightArcVal = fUtilities.computeArcVal(beachLinePos, breakPoint, node->getSuccessor()->getEvent());
867 
868  dcel2d::HalfEdge* halfEdge = node->getHalfEdge();
869  dcel2d::HalfEdge* halfTwin = halfEdge->getTwinHalfEdge();
870  dcel2d::Vertex* vertex = halfEdge->getTargetVertex();
871 
872  std::cout << "node " << nodeCount << " has break: " << breakPoint << ", beachPos: " << beachLinePos << " (last: " << lastBreakPoint << "), leftArcVal: " << leftArcVal << ", rightArcVal: " << rightArcVal << std::endl;
873  if (lastBreakPoint > breakPoint) std::cout << " ***** lastBreakPoint larger than current break point *****" << std::endl;
874  std::cout << " left arc x/y: " << node->getPredecessor()->getEvent()->xPos() << "/" << node->getPredecessor()->getEvent()->yPos() << ", right arc x/y: " << node->getSuccessor()->getEvent()->xPos() << "/" << node->getSuccessor()->getEvent()->yPos() << std::endl;
875  std::cout << " halfEdge: " << halfEdge << ", target vtx: " << vertex << ", face: " << halfEdge->getFace() << ", twin: " << halfTwin << ", twin tgt: " << halfTwin->getTargetVertex() << ", twin face: " << halfTwin->getFace() << ", next: " << halfEdge->getNextHalfEdge() << ", last: " << halfEdge->getLastHalfEdge() << std::endl;
876 
877  const dcel2d::Coords& leftLeafPos = node->getPredecessor()->getEvent()->getCoords();
878  const dcel2d::Coords& rightLeafPos = node->getSuccessor()->getEvent()->getCoords();
879  Eigen::Vector3f lrPosDiff = rightLeafPos - leftLeafPos;
880  Eigen::Vector3f lrPosSum = rightLeafPos + leftLeafPos;
881 
882  lrPosDiff.normalize();
883  lrPosSum *= 0.5;
884 
885  dcel2d::Coords leafPos = node->getSuccessor()->getEvent()->getCoords();
886  dcel2d::Coords leafDir = lrPosDiff;
887 
888  if (vertex)
889  {
890  dcel2d::Coords breakDir(leafDir[1],-leafDir[0],0.);
891  dcel2d::Coords breakPos = lrPosSum;
892  dcel2d::Coords vertexPos = vertex->getCoords();
893 
894  // Get the arclength from the break position to the intersection of the two lines
895  dcel2d::Coords breakToLeafPos = leafPos - vertexPos;
896  double arcLenToLine = breakToLeafPos.dot(breakDir);
897 
898  // Now get position
899  dcel2d::Coords breakVertexPos = vertexPos + arcLenToLine * breakDir;
900 
901  std::cout << " halfEdge position: " << breakPos[0] << "/" << breakPos[1] << ", vertex: " << vertexPos[0] << "/" << vertexPos[1] << ", end: " << breakVertexPos[0] << "/" << breakVertexPos[1] << ", dir: " << breakDir[0] << "/" << breakDir[1] << ", arclen: " << arcLenToLine << std::endl;
902 
903  // At this point we need to create a vertex and then terminate the half edges here...
904  fVertexList.push_back(dcel2d::Vertex(breakVertexPos,halfEdge));
905 
906  dcel2d::Vertex* breakVertex = &fVertexList.back();
907 
908  halfTwin->setTargetVertex(breakVertex);
909  }
910  else
911  {
912  std::cout << "****** null vertex!!! Skipping to next node... *********" << std::endl;
913  }
914 
915  if (lastBreakPoint > breakPoint) nBadBreaks++;
916 
917  lastBreakPoint = breakPoint;
918  }
919 
920  if (node->getAssociated())
921  {
922  BSTNode* associated = node->getAssociated();
923  IEvent* iEvent = associated->getEvent();
924 
925  std::cout << " -> associated circle: " << iEvent->isCircle() << ", is valid: " << iEvent->isValid() << std::endl;
926  }
927 
928  node = node->getSuccessor();
929  nodeCount++;
930  }
931 
932  // Now that we have the convex hull, loop over vertices to see if they are inside or outside of the convex hull
933  dcel2d::VertexList::iterator curVertexItr = fVertexList.begin();
934 
935  size_t nVerticesInitial = fVertexList.size();
936 
937  // Loop over all vertices to begin with
938  while(curVertexItr != fVertexList.end())
939  {
940  // Dereference vertex
941  dcel2d::Vertex& vertex = *curVertexItr;
942 
943  bool outsideHull = !isInsideConvexHull(vertex);
944 
945  // Do we need to drop this vertex?
946  if (outsideHull)
947  {
948  curVertexItr = fVertexList.erase(curVertexItr);
949  }
950  else curVertexItr++;
951  }
952 
954  ComputeFaceArea();
955 
956  std::cout << "Loop over beachline done, saved " << fConvexHullList.size() << " arcs, encountered " << nBadBreaks << " bad break points" << std::endl;
957  std::cout << "-- started with " << nVerticesInitial << " vertices, found " << fVertexList.size() << " inside convex hull" << std::endl;
958  }
959 
960  return;
961 }
962 
964 {
965  // Assume the input node is the top of the binary search tree and represents
966  // the beach line at the end of the sweep algorithm
967  // The convex hull will then be the leaf elements along the beach line.
968  // Note that this algorithm will give you the convex hull in a clockwise manner
969  if (topNode)
970  {
971  const BSTNode* node = topNode;
972 
973  // Initialize the center
974  fConvexHullCenter = dcel2d::Coords(0.,0.,0.);
975 
976  // Run down the left branches to find the right most leaf
977  // We do it this way to make sure we get a CCW convex hull
978  while(node->getRightChild()) node = node->getRightChild();
979 
980  // Include a check on convexity...
981  Eigen::Vector3f prevVec(0.,0.,0.);
982  dcel2d::Coords lastPoint(0.,0.,0.);
983 
984  // Now we are going to traverse across the beach line and process the remaining leaves
985  // This will identify the convex hull
986  while(node)
987  {
988  if (!node->getLeftChild() && !node->getRightChild())
989  {
990  // Add point to the convex hull list
991  fConvexHullList.emplace_back(node->getEvent()->getPoint());
992 
993  // Mark the face as on the convex hull
994  node->getFace()->setOnConvexHull();
995 
996  dcel2d::Coords nextPoint = node->getEvent()->getCoords();
997  Eigen::Vector3f curVec = nextPoint - lastPoint;
998 
999  curVec.normalize();
1000 
1001  double dotProd = prevVec.dot(curVec);
1002 
1003  std::cout << "--> lastPoint: " << lastPoint[0] << "/" << lastPoint[1] << ", tan: " << std::atan2(lastPoint[1],lastPoint[0]) << ", curPoint: " << nextPoint[0] << "/" << nextPoint[1] << ", tan: " << std::atan2(nextPoint[1],nextPoint[0]) << ", dot: " << dotProd << std::endl;
1004 
1005  prevVec = curVec;
1006  lastPoint = nextPoint;
1007  }
1008 
1009  node = node->getPredecessor();
1010  }
1011 
1012  // Annoyingly, our algorithm does not contain only the convex hull points and so we need to skim out the renegades...
1014 
1015  for(const auto& edgePoint : fConvexHullList) localList.emplace_back(std::get<0>(edgePoint),std::get<1>(edgePoint),std::get<2>(edgePoint));
1016 
1017  // Sort the point vec by increasing x, then increase y
1018  localList.sort([](const auto& left, const auto& right){return (std::abs(std::get<0>(left) - std::get<0>(right)) > std::numeric_limits<float>::epsilon()) ? std::get<0>(left) < std::get<0>(right) : std::get<1>(left) < std::get<1>(right);});
1019 
1020  // Why do I need to do this?
1021  lar_cluster3d::ConvexHull convexHull(localList);
1022 
1023  // Clear the convex hull list...
1024 // fConvexHullList.clear();
1025 
1026  std::cout << "~~~>> there are " << convexHull.getConvexHull().size() << " convex hull points and " << fConvexHullList.size() << " infinite cells" << std::endl;
1027 
1028  // Now rebuild it
1029  for(const auto& hullPoint : convexHull.getConvexHull())
1030  {
1031 // fConvexHullList.emplace_back(std::get<0>(hullPoint),std::get<1>(hullPoint),std::get<2>(hullPoint));
1032 
1033  std::cout << "~~~ Convex hull Point: " << std::get<0>(hullPoint) << ", " << std::get<1>(hullPoint) << std::endl;
1034  }
1035  }
1036 
1037  return;
1038 }
1039 
1041 {
1042  dcel2d::PointList::const_iterator nextPointItr = fConvexHullList.begin();
1043  dcel2d::PointList::const_iterator firstPointItr = nextPointItr++;
1044 
1045  float maxSeparation(0.);
1046 
1047  PointPair extremePoints(dcel2d::Point(0,0,NULL),dcel2d::Point(0,0,NULL));
1048 
1049  while(nextPointItr != fConvexHullList.end())
1050  {
1051  // Get a vector representing the edge from the first to the current next point
1052  dcel2d::Point firstPoint = *firstPointItr++;
1053  dcel2d::Point nextPoint = *nextPointItr;
1054  Eigen::Vector2f firstEdge(std::get<0>(*firstPointItr) - std::get<0>(firstPoint), std::get<1>(*firstPointItr) - std::get<1>(firstPoint));
1055 
1056  // normalize it
1057  firstEdge.normalize();
1058 
1059  dcel2d::PointList::const_iterator endPointItr = nextPointItr;
1060 
1061  while(++endPointItr != fConvexHullList.end())
1062  {
1063  dcel2d::Point endPoint = *endPointItr;
1064  Eigen::Vector2f nextEdge(std::get<0>(endPoint) - std::get<0>(nextPoint), std::get<1>(endPoint) - std::get<1>(nextPoint));
1065 
1066  // normalize it
1067  nextEdge.normalize();
1068 
1069  // Have we found the turnaround point?
1070  if (firstEdge.dot(nextEdge) < 0.)
1071  {
1072  Eigen::Vector2f separation(std::get<0>(nextPoint) - std::get<0>(firstPoint), std::get<1>(nextPoint) - std::get<1>(firstPoint));
1073  float separationDistance = separation.norm();
1074 
1075  if (separationDistance > maxSeparation)
1076  {
1077  extremePoints.first = firstPoint;
1078  extremePoints.second = nextPoint;
1079  maxSeparation = separationDistance;
1080  }
1081 
1082  // Regardless of thise being the maximum distance we have hit a turnaround point so
1083  // we need to break out of this loop
1084  break;
1085  }
1086 
1087  nextPointItr = endPointItr;
1088  nextPoint = endPoint;
1089  }
1090 
1091  // If we have hit the end of the convex hull without finding a turnaround point then we are not
1092  // going to find one so break out of the main loop
1093  if (endPointItr == fConvexHullList.end()) break;
1094 
1095  // Need to make sure we don't overrun the next point
1096  if (firstPointItr == nextPointItr) nextPointItr++;
1097  }
1098 
1099  return extremePoints;
1100 }
1101 
1103 {
1104  bool insideHull(true);
1105  dcel2d::Point vertexPos(vertex.getCoords()[0],vertex.getCoords()[1],NULL);
1106 
1107  // Now check to see if the vertex is inside the convex hull
1108  dcel2d::PointList::const_iterator hullItr = fConvexHullList.begin();
1109  dcel2d::Point firstPoint = *hullItr++;
1110 
1111  // We assume here the convex hull is stored in a CCW order
1112  while(hullItr != fConvexHullList.end())
1113  {
1114  dcel2d::Point secondPoint = *hullItr++;
1115 
1116  // CCW order means we check to see if this point lies to left of line from first to second point
1117  // in order for the point to be "inside" the convex hull
1118  // Note that we are looking to reject points that are outside...
1119  if (!isLeft(firstPoint,secondPoint,vertexPos))
1120  {
1121  insideHull = false;
1122  break;
1123  }
1124 
1125  firstPoint = secondPoint;
1126  }
1127 
1128  return insideHull;
1129 }
1130 
1132  dcel2d::PointList::const_iterator firstHullPointItr,
1133  dcel2d::Coords& intersection,
1134  double& distToConvexHull) const
1135 {
1136  bool outsideHull(false);
1137  dcel2d::Point vertexPos(vertex.getCoords()[0],vertex.getCoords()[1],NULL);
1138 
1139  distToConvexHull = std::numeric_limits<double>::max();
1140  firstHullPointItr = fConvexHullList.begin();
1141 
1142  // Now check to see if the vertex is inside the convex hull
1143  dcel2d::PointList::const_iterator hullItr = firstHullPointItr;
1144  dcel2d::PointList::const_iterator firstPointItr = hullItr++;
1145 
1146  while(hullItr != fConvexHullList.end())
1147  {
1148  dcel2d::PointList::const_iterator secondPointItr = hullItr++;
1149 
1150  // Dereference some stuff
1151  double xPrevToPoint = (std::get<0>(vertexPos) - std::get<0>(*firstPointItr));
1152  double yPrevToPoint = (std::get<1>(vertexPos) - std::get<1>(*firstPointItr));
1153  double xPrevToCur = (std::get<0>(*secondPointItr) - std::get<0>(*firstPointItr));
1154  double yPrevToCur = (std::get<1>(*secondPointItr) - std::get<1>(*firstPointItr));
1155  double edgeLength = std::sqrt(xPrevToCur * xPrevToCur + yPrevToCur * yPrevToCur);
1156 
1157  // Find projection onto convex hull edge
1158  double projection = ((xPrevToPoint * xPrevToCur) + (yPrevToPoint * yPrevToCur)) / edgeLength;
1159 
1160  // DOCA point
1161  dcel2d::Point docaPoint(std::get<0>(*firstPointItr) + projection * xPrevToCur / edgeLength,
1162  std::get<1>(*firstPointItr) + projection * yPrevToCur / edgeLength, 0);
1163 
1164  if (projection > edgeLength) docaPoint = *secondPointItr;
1165  if (projection < 0) docaPoint = *firstPointItr;
1166 
1167  double xDocaDist = std::get<0>(vertexPos) - std::get<0>(docaPoint);
1168  double yDocaDist = std::get<1>(vertexPos) - std::get<1>(docaPoint);
1169  double docaDist = xDocaDist * xDocaDist + yDocaDist * yDocaDist;
1170 
1171  if (docaDist < distToConvexHull)
1172  {
1173  firstHullPointItr = firstPointItr;
1174  intersection = dcel2d::Coords(std::get<0>(docaPoint),std::get<1>(docaPoint),0.);
1175  distToConvexHull = docaDist;
1176  }
1177 
1178  // Check to see if this point is outside the convex hull
1179  if (isLeft(*firstPointItr,*secondPointItr,vertexPos)) outsideHull = true;
1180 
1181  firstPointItr = secondPointItr;
1182  }
1183 
1184  return outsideHull;
1185 }
1186 
1188 {
1189  dcel2d::HalfEdgeList::iterator edgeItr = fHalfEdgeList.begin();
1190 
1191  while(edgeItr != fHalfEdgeList.end())
1192  {
1193  dcel2d::HalfEdge* halfEdge = &(*edgeItr);
1194  dcel2d::HalfEdge* twinEdge = halfEdge->getTwinHalfEdge();
1195 
1196  // Make sure we are not looking at an infinite edge
1197  if (halfEdge->getTargetVertex() && twinEdge->getTargetVertex())
1198  {
1199  dcel2d::Coords vtxPosDiff = halfEdge->getTargetVertex()->getCoords() - twinEdge->getTargetVertex()->getCoords();
1200 
1201  if (vtxPosDiff.norm() < 1.e-3)
1202  {
1203  std::cout << "***>> found a degenerate vertex! " << halfEdge->getTargetVertex()->getCoords()[0] << "/" << halfEdge->getTargetVertex()->getCoords()[1] << ", d: " << vtxPosDiff.norm() << std::endl;
1204  edgeItr++;
1205  }
1206  else edgeItr++;
1207  }
1208  else edgeItr++;
1209  }
1210 
1211  return;
1212 }
1213 
1214 
1216 {
1217  // Compute the area by taking advantage of
1218  // 1) the ability to decompose a convex hull into triangles,
1219  // 2) the ability to use the cross product to calculate the area
1220  // So the idea is to loop through all the faces and then follow the edges
1221  // around the face to compute the area of the face.
1222  // Note that a special case are the "infinite faces" which lie on the
1223  // convex hull of the event. Skip these for now...
1224 
1225  double totalArea(0.);
1226  int nNonInfiniteFaces(0);
1227  double smallestArea(std::numeric_limits<double>::max());
1228  double largestArea(0.);
1229 
1230  std::vector<std::pair<double,const dcel2d::Face*>> areaFaceVec;
1231 
1232  areaFaceVec.reserve(fFaceList.size());
1233 
1234  for(auto& face : fFaceList)
1235  {
1236 // const dcel2d::Coords& faceCoords = face.getCoords();
1237  const dcel2d::HalfEdge* halfEdge = face.getHalfEdge();
1238  double faceArea(0.);
1239  int numEdges(0);
1240  bool doNext = true;
1241 
1242  dcel2d::Coords faceCenter(0.,0.,0.);
1243 
1244  while(doNext)
1245  {
1246  if (halfEdge->getTargetVertex())
1247  faceCenter += halfEdge->getTargetVertex()->getCoords();
1248 
1249  numEdges++;
1250 
1251  halfEdge = halfEdge->getNextHalfEdge();
1252 
1253  if (!halfEdge)
1254  {
1255  faceArea = std::numeric_limits<double>::max();
1256  doNext = false;
1257  }
1258 
1259  if (halfEdge == face.getHalfEdge()) doNext = false;
1260  }
1261 
1262  faceCenter /= numEdges;
1263 
1264  halfEdge = face.getHalfEdge();
1265  doNext = true;
1266 
1267  while(doNext)
1268  {
1269  const dcel2d::HalfEdge* twinEdge = halfEdge->getTwinHalfEdge();
1270 
1271  if (!halfEdge->getTargetVertex() || !twinEdge->getTargetVertex())
1272  {
1273  faceArea = std::numeric_limits<double>::max();
1274  break;
1275  }
1276 
1277  // Recover the two vertex points
1278  const dcel2d::Coords& p1 = halfEdge->getTargetVertex()->getCoords();
1279  const dcel2d::Coords& p2 = twinEdge->getTargetVertex()->getCoords();
1280 
1281  // Define a quick 2D cross product here since it will used quite a bit!
1282  double dp1p0X = p1[0] - faceCenter[0];
1283  double dp1p0Y = p1[1] - faceCenter[1];
1284  double dp2p0X = p2[0] - faceCenter[0];
1285  double dp2p0Y = p2[1] - faceCenter[1];
1286 
1287  //faceArea += dp1p0X * dp2p0Y - dp1p0Y * dp2p0X;
1288  double crossProduct = dp1p0X * dp2p0Y - dp1p0Y * dp2p0X;
1289 
1290  faceArea += crossProduct;
1291 
1292  if (crossProduct < 0.)
1293  {
1294  dcel2d::Coords edgeVec = p1 - p2;
1295  std::cout << "--- negative cross: " << crossProduct << ", edgeLen: " << edgeVec.norm() << ", x/y: " << edgeVec[0] << "/" << edgeVec[1] << std::endl;
1296  }
1297 
1298 // numEdges++;
1299 
1300  halfEdge = halfEdge->getNextHalfEdge();
1301 
1302  if (!halfEdge)
1303  {
1304  faceArea = std::numeric_limits<double>::max();
1305  break;
1306  }
1307 
1308  if (halfEdge == face.getHalfEdge()) doNext = false;
1309  }
1310 
1311  areaFaceVec.emplace_back(faceArea,&face);
1312 
1313  if (faceArea < std::numeric_limits<double>::max() && faceArea > 0.)
1314  {
1315  nNonInfiniteFaces++;
1316  totalArea += faceArea;
1317  smallestArea = std::min(faceArea,smallestArea);
1318  largestArea = std::max(faceArea,largestArea);
1319  }
1320 
1321  if (faceArea < 1.e-4) std::cout << "---> face area <= 0: " << faceArea << ", with " << numEdges << " edges" << std::endl;
1322 
1323  face.setFaceArea(faceArea);
1324  }
1325 
1326  // Calculate a truncated mean...
1327  std::sort(areaFaceVec.begin(),areaFaceVec.end(),[](const auto& left, const auto& right){return left.first < right.first;});
1328 
1329  std::vector<std::pair<double,const dcel2d::Face*>>::iterator firstItr = std::find_if(areaFaceVec.begin(),areaFaceVec.end(),[](const auto& val){return val.first > 0.;});
1330  std::vector<std::pair<double,const dcel2d::Face*>>::iterator lastItr = std::find_if(areaFaceVec.begin(),areaFaceVec.end(),[](const auto& val){return !(val.first < std::numeric_limits<double>::max());});
1331 
1332  size_t nToKeep = 0.8 * std::distance(firstItr,lastItr);
1333 
1334  std::cout << ">>>>> nToKeep: " << nToKeep << ", last non infinite entry: " << std::distance(areaFaceVec.begin(),lastItr) << std::endl;
1335 
1336  double totalTruncArea = std::accumulate(firstItr,firstItr+nToKeep,0.,[](auto sum, const auto& val){return sum+val.first;});
1337  double aveTruncArea = totalTruncArea / double(nToKeep);
1338 
1339  if (nNonInfiniteFaces > 0) std::cout << ">>>> Face area for " << nNonInfiniteFaces << ", ave area: " << totalArea / nNonInfiniteFaces << ", ave trunc area: " << aveTruncArea << ", ratio: " << totalTruncArea / totalArea << ", smallest: " << smallestArea << ", largest: " << largestArea << std::endl;
1340  else std::cout << ">>>>> there are no non infinite faces" << std::endl;
1341 
1342  return totalArea;
1343 }
1344 
1345 
1347 {
1348  // Find extremes in x to start
1349  std::pair<dcel2d::VertexList::const_iterator,dcel2d::VertexList::const_iterator> minMaxItrX = std::minmax_element(vertexList.begin(),vertexList.end(),[](const auto& left, const auto& right){return left.getCoords()[0] < right.getCoords()[0];});
1350 
1351  fXMin = minMaxItrX.first->getCoords()[0];
1352  fXMax = minMaxItrX.second->getCoords()[0];
1353 
1354  // To get the extremes in y we need to make a pass through the list
1355  std::pair<dcel2d::VertexList::const_iterator,dcel2d::VertexList::const_iterator> minMaxItrY = std::minmax_element(vertexList.begin(),vertexList.end(),[](const auto& left, const auto& right){return left.getCoords()[1] < right.getCoords()[1];});
1356 
1357  fYMin = minMaxItrY.first->getCoords()[1];
1358  fYMax = minMaxItrY.second->getCoords()[1];
1359 
1360  return;
1361 }
1362 
1364 {
1365  // The idea is to find the nearest edge of the convex hull, defined by
1366  // two adjacent vertices of the hull, to the input point.
1367  // As near as I can tell, the best way to do this is to apply brute force...
1368  // Idea will be to iterate over pairs of points
1369  dcel2d::PointList::const_iterator curPointItr = fPointList.begin();
1370  dcel2d::Point prevPoint = *curPointItr++;
1371  dcel2d::Point curPoint = *curPointItr;
1372 
1373  // Set up the winner
1374  PointPair closestEdge(prevPoint,curPoint);
1375 
1376  closestDistance = std::numeric_limits<double>::max();
1377 
1378  // curPointItr is meant to point to the second point
1379  while(curPointItr != fPointList.end())
1380  {
1381  if (curPoint != prevPoint)
1382  {
1383  // Dereference some stuff
1384  double xPrevToPoint = (std::get<0>(point) - std::get<0>(prevPoint));
1385  double yPrevToPoint = (std::get<1>(point) - std::get<1>(prevPoint));
1386  double xPrevToCur = (std::get<0>(curPoint) - std::get<0>(prevPoint));
1387  double yPrevToCur = (std::get<1>(curPoint) - std::get<1>(prevPoint));
1388  double edgeLength = std::sqrt(xPrevToCur * xPrevToCur + yPrevToCur * yPrevToCur);
1389 
1390  // Find projection onto convex hull edge
1391  double projection = ((xPrevToPoint * xPrevToCur) + (yPrevToPoint * yPrevToCur)) / edgeLength;
1392 
1393  // DOCA point
1394  dcel2d::Point docaPoint(std::get<0>(prevPoint) + projection * xPrevToCur / edgeLength,
1395  std::get<1>(prevPoint) + projection * yPrevToCur / edgeLength, 0);
1396 
1397  if (projection > edgeLength) docaPoint = curPoint;
1398  if (projection < 0) docaPoint = prevPoint;
1399 
1400  double xDocaDist = std::get<0>(point) - std::get<0>(docaPoint);
1401  double yDocaDist = std::get<1>(point) - std::get<1>(docaPoint);
1402  double docaDist = xDocaDist * xDocaDist + yDocaDist * yDocaDist;
1403 
1404  if (docaDist < closestDistance)
1405  {
1406  closestEdge = PointPair(prevPoint,curPoint);
1407  closestDistance = docaDist;
1408  }
1409  }
1410 
1411  prevPoint = curPoint;
1412  curPoint = *curPointItr++;
1413  }
1414 
1415  closestDistance = std::sqrt(closestDistance);
1416 
1417  // Convention is convex hull vertices sorted in counter clockwise fashion so if the point
1418  // lays to the left of the nearest edge then it must be an interior point
1419  if (isLeft(closestEdge.first,closestEdge.second,point)) closestDistance = -closestDistance;
1420 
1421  return closestEdge;
1422 }
1423 
1425 {
1426  double closestDistance;
1427 
1428  findNearestEdge(point,closestDistance);
1429 
1430  return closestDistance;
1431 }
1432 
1433 } // namespace lar_cluster3d
process_name vertex
Definition: cheaterreco.fcl:51
This defines the actual beach line. The idea is to implement this as a self balancing binary search t...
Definition: BeachLine.h:124
bool computeCircleCenter2(const dcel2d::Coords &, const dcel2d::Coords &, const dcel2d::Coords &, dcel2d::Coords &, double &, double &) const
Definition: Voronoi.cxx:762
HalfEdge * getLastHalfEdge() const
Definition: DCEL.h:165
HalfEdge * getNextHalfEdge() const
Definition: DCEL.h:164
dcel2d::HalfEdgeList & fHalfEdgeList
Definition: Voronoi.h:185
std::list< HalfEdge > HalfEdgeList
Definition: DCEL.h:184
void setHalfEdge(HalfEdge *half)
Definition: DCEL.h:81
HalfEdge * getTwinHalfEdge() const
Definition: DCEL.h:163
BSTNode class definiton specifically for use in constructing Voronoi diagrams. We are trying to follo...
Definition: BeachLine.h:32
walls no right
Definition: selectors.fcl:105
Face * getFace() const
Definition: DCEL.h:162
double closestDistance(const TVector3 &line0, const TVector3 &line1, const TVector3 &p)
std::list< Point > PointList
The list of the projected points.
Definition: ConvexHull.h:34
bool isInsideConvexHull(const dcel2d::Vertex &) const
Is a vertex inside the convex hull - meant to be a fast check.
Definition: Voronoi.cxx:1102
virtual void setInvalid() const =0
Interface for configuring the particular algorithm tool.
virtual bool isCircle() const =0
virtual bool isValid() const =0
double Area() const
Computes the area of the enclosed convext hull.
Definition: Voronoi.cxx:104
dcel2d::FaceList & fFaceList
Definition: Voronoi.h:187
std::list< BSTNode > BSTNodeList
Definition: BeachLine.h:117
~VoronoiDiagram()
Destructor.
Definition: Voronoi.cxx:76
void makeLeftCircleEvent(EventQueue &, BSTNode *, double)
Definition: Voronoi.cxx:557
double computeArcVal(const double, const double, const IEvent *) const
bool computeCircleCenter(const dcel2d::Coords &, const dcel2d::Coords &, const dcel2d::Coords &, dcel2d::Coords &, double &, double &) const
Definition: Voronoi.cxx:690
BSTNodeList fCircleNodeList
Definition: Voronoi.h:192
void setLastHalfEdge(HalfEdge *last)
Definition: DCEL.h:171
BSTNode * getAssociated() const
Definition: BeachLine.h:78
int countLeaves() const
Definition: BeachLine.cxx:303
void setTwinHalfEdge(HalfEdge *twin)
Definition: DCEL.h:169
Implements a ConvexHull for use in clustering.
T abs(T value)
IEvent * makeCircleEvent(BSTNode *, BSTNode *, BSTNode *, double)
There are two types of events in the queue, here we handle circle events.
Definition: Voronoi.cxx:653
void handleCircleEvents(BeachLine &, EventQueue &, IEvent *)
There are two types of events in the queue, here we handle circle events.
Definition: Voronoi.cxx:472
double distance(geo::Point_t const &point, CathodeDesc_t const &cathode)
Returns the distance of a point from the cathode.
std::list< Face > FaceList
Definition: DCEL.h:183
CircleEventList fCircleEventList
Definition: Voronoi.h:191
virtual const dcel2d::Coords & circleCenter() const =0
SiteEventList fSiteEventList
Definition: Voronoi.h:190
double ComputeFaceArea()
Compute the area of the faces.
Definition: Voronoi.cxx:1215
EventUtilities fUtilities
Definition: Voronoi.h:204
std::priority_queue< IEvent *, std::vector< IEvent * >, bool(*)(const IEvent *, const IEvent *)> EventQueue
Definition: Voronoi.h:95
std::tuple< double, double, const reco::ClusterHit3D * > Point
Definitions used by the VoronoiDiagram algorithm.
Definition: DCEL.h:44
BSTNode * insertNewLeaf(IEvent *)
Definition: BeachLine.cxx:55
void buildVoronoiDiagram(const dcel2d::PointList &)
Given an input set of 2D points construct a 2D voronoi diagram.
Definition: Voronoi.cxx:133
bool compareSiteEventPtrs(const IEvent *left, const IEvent *right)
Definition: Voronoi.cxx:129
double findNearestDistance(const dcel2d::Point &) const
Given an input point, find the distance to the nearest edge/point.
Definition: Voronoi.cxx:1424
void findBoundingBox(const dcel2d::VertexList &)
Find the min/max values in x-y to use as a bounding box.
Definition: Voronoi.cxx:1346
dcel2d::VertexList & fVertexList
Definition: Voronoi.h:186
BSTNode * getRightChild() const
Definition: BeachLine.h:75
walls no left
Definition: selectors.fcl:105
void mergeDegenerateVertices()
merge degenerate vertices (found by zero length edges)
Definition: Voronoi.cxx:1187
const PointList & getConvexHull() const
recover the list of convex hull vertices
Definition: ConvexHull.h:58
dcel2d::Coords fConvexHullCenter
Definition: Voronoi.h:195
void makeRightCircleEvent(EventQueue &, BSTNode *, double)
Definition: Voronoi.cxx:606
BSTNode * removeLeaf(BSTNode *)
Definition: BeachLine.cxx:204
void setAssociated(BSTNode *node)
Definition: BeachLine.h:91
virtual BSTNode * getBSTNode() const =0
virtual double yPos() const =0
dcel2d::PointList fPointList
Definition: Voronoi.h:189
const BSTNode * getTopNode() const
Definition: BeachLine.h:131
ConvexHull class definiton.
Definition: ConvexHull.h:27
void setFace(Face *face)
Definition: DCEL.h:168
double crossProduct(const dcel2d::Point &p0, const dcel2d::Point &p1, const dcel2d::Point &p2) const
Gets the cross product of line from p0 to p1 and p0 to p2.
Definition: Voronoi.cxx:91
PointPair findNearestEdge(const dcel2d::Point &, double &) const
Given an input Point, find the nearest edge.
Definition: Voronoi.cxx:1363
virtual const dcel2d::Coords & getCoords() const =0
const HalfEdge * getHalfEdge() const
Definition: DCEL.h:110
int traverseBeach() const
Definition: BeachLine.cxx:343
dcel2d::PointList fConvexHullList
Definition: Voronoi.h:194
void setNextHalfEdge(HalfEdge *next)
Definition: DCEL.h:170
std::pair< dcel2d::Point, dcel2d::Point > PointPair
Definition: Voronoi.h:34
virtual double xPos() const =0
double computeBreak(const double, const IEvent *, const IEvent *, RootsPair &) const
void setHalfEdge(HalfEdge *half)
Definition: DCEL.h:116
const dcel2d::PointList & getConvexHull() const
recover the point list representing the convex hull
Definition: Voronoi.h:76
void boostTranslation(const dcel2d::PointList &, const boost::polygon::voronoi_edge< double > *, const boost::polygon::voronoi_edge< double > *, BoostEdgeToEdgeMap &, BoostVertexToVertexMap &, BoostCellToFaceMap &)
Definition: Voronoi.cxx:321
PointPair getExtremePoints() const
Given an input Point, find the nearest edge.
Definition: Voronoi.cxx:1040
BSTNode * getPredecessor() const
Definition: BeachLine.h:76
do i e
std::map< const boost::polygon::voronoi_edge< double > *, dcel2d::HalfEdge * > BoostEdgeToEdgeMap
Translate boost to dcel.
Definition: Voronoi.h:149
std::map< const boost::polygon::voronoi_vertex< double > *, dcel2d::Vertex * > BoostVertexToVertexMap
Definition: Voronoi.h:150
bool computeCircleCenter3(const dcel2d::Coords &, const dcel2d::Coords &, const dcel2d::Coords &, dcel2d::Coords &, double &, double &) const
Definition: Voronoi.cxx:793
std::pair< double, double > RootsPair
void terminateInfiniteEdges(BeachLine &, double)
this aims to process remaining elements in the beachline after the event queue has been depleted ...
Definition: Voronoi.cxx:829
dcel2d::Face * getFace() const
Definition: BeachLine.h:81
std::list< Point > PointList
Definition: DCEL.h:45
void buildVoronoiDiagramBoost(const dcel2d::PointList &)
Definition: Voronoi.cxx:265
void setFace(dcel2d::Face *face)
Definition: BeachLine.h:94
dcel2d::HalfEdge * getHalfEdge() const
Definition: BeachLine.h:80
Vertex * getTargetVertex() const
Definition: DCEL.h:161
Eigen::Vector3f Coords
Definition: DCEL.h:46
void setOnConvexHull()
Definition: DCEL.h:117
virtual const dcel2d::Point & getPoint() const =0
const Coords & getCoords() const
Definition: DCEL.h:69
std::list< Vertex > VertexList
Definition: DCEL.h:182
void setTargetVertex(Vertex *vertex)
Definition: DCEL.h:167
IEvent * getEvent() const
Definition: BeachLine.h:72
physics associatedGroupsWithLeft p1
BSTNode * getSuccessor() const
Definition: BeachLine.h:77
BEGIN_PROLOG could also be cout
BSTNode * getLeftChild() const
Definition: BeachLine.h:74
std::map< const boost::polygon::voronoi_cell< double > *, dcel2d::Face * > BoostCellToFaceMap
Definition: Voronoi.h:151
VoronoiDiagram(dcel2d::HalfEdgeList &, dcel2d::VertexList &, dcel2d::FaceList &)
Constructor.
Definition: Voronoi.cxx:51
bool isOutsideConvexHull(const dcel2d::Vertex &, dcel2d::PointList::const_iterator, dcel2d::Coords &, double &) const
is vertex outside the convex hull and if so return some useful information
Definition: Voronoi.cxx:1131
void setHalfEdge(dcel2d::HalfEdge *half)
Definition: BeachLine.h:93
bool isLeft(const dcel2d::Point &p0, const dcel2d::Point &p1, const dcel2d::Point &pCheck) const
Determines whether a point is to the left, right or on line specifiec by p0 and p1.
Definition: Voronoi.cxx:82
void handleSiteEvents(BeachLine &, EventQueue &, IEvent *)
There are two types of events in the queue, here we handle site events.
Definition: Voronoi.cxx:415