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