ClusterPathFinder_tool.cc
Go to the documentation of this file.
1 /**
2  * @file ClusterPathFinder_tool.cc
3  *
4  * @brief art Tool for comparing clusters and merging those that are consistent
5  *
6  */
7 
8 // Framework Includes
11 #include "cetlib/cpu_timer.h"
13 
17 
18 // LArSoft includes
21 
22 // Eigen
23 #include <Eigen/Core>
24 
25 // std includes
26 #include <string>
27 #include <iostream>
28 #include <memory>
29 
30 //------------------------------------------------------------------------------------------------------------------------------------------
31 // implementation follows
32 
33 namespace lar_cluster3d {
34 
35 class ClusterPathFinder : virtual public IClusterModAlg
36 {
37 public:
38  /**
39  * @brief Constructor
40  *
41  * @param pset
42  */
43  explicit ClusterPathFinder(const fhicl::ParameterSet&);
44 
45  /**
46  * @brief Destructor
47  */
49 
50  void configure(fhicl::ParameterSet const &pset) override;
51 
52  /**
53  * @brief Interface for initializing histograms if they are desired
54  * Note that the idea is to put hisgtograms in a subfolder
55  *
56  * @param TFileDirectory - the folder to store the hists in
57  */
58  void initializeHistograms(art::TFileDirectory&) override;
59 
60  /**
61  * @brief Scan an input collection of clusters and modify those according
62  * to the specific implementing algorithm
63  *
64  * @param clusterParametersList A list of cluster objects (parameters from associated hits)
65  */
66  void ModifyClusters(reco::ClusterParametersList&) const override;
67 
68  /**
69  * @brief If monitoring, recover the time to execute a particular function
70  */
71  float getTimeToExecute() const override {return m_timeToProcess;}
72 
73 private:
74 
75  /**
76  * @brief Use PCA to try to find path in cluster
77  *
78  * @param clusterParameters The given cluster parameters object to try to split
79  * @param clusterParametersList The list of clusters
80  */
83  reco::ClusterParametersList& outputClusterList,
84  int level = 0) const;
85 
86  float closestApproach(const Eigen::Vector3f&,
87  const Eigen::Vector3f&,
88  const Eigen::Vector3f&,
89  const Eigen::Vector3f&,
90  Eigen::Vector3f&,
91  Eigen::Vector3f&) const;
92 
93  using Point = std::tuple<float,float,const reco::ClusterHit3D*>;
94  using PointList = std::list<Point>;
95  using MinMaxPoints = std::pair<Point,Point>;
96  using MinMaxPointPair = std::pair<MinMaxPoints,MinMaxPoints>;
97 
98  void buildConvexHull(reco::ClusterParameters& clusterParameters, int level = 0) const;
99  void buildVoronoiDiagram(reco::ClusterParameters& clusterParameters, int level = 0) const;
100  /**
101  * @brief Data members to follow
102  */
104  size_t m_minTinyClusterSize; ///< Minimum size for a "tiny" cluster
105  mutable float m_timeToProcess; ///<
106 
107  std::unique_ptr<lar_cluster3d::IClusterAlg> m_clusterAlg; ///< Algorithm to do 3D space point clustering
108  PrincipalComponentsAlg m_pcaAlg; // For running Principal Components Analysis
109 };
110 
112  m_pcaAlg(pset.get<fhicl::ParameterSet>("PrincipalComponentsAlg"))
113 {
114  this->configure(pset);
115 }
116 
117 //------------------------------------------------------------------------------------------------------------------------------------------
118 
120 {
121 }
122 
123 //------------------------------------------------------------------------------------------------------------------------------------------
124 
126 {
127  m_enableMonitoring = pset.get<bool> ("EnableMonitoring", true );
128  m_minTinyClusterSize = pset.get<size_t>("MinTinyClusterSize",40);
129  m_clusterAlg = art::make_tool<lar_cluster3d::IClusterAlg>(pset.get<fhicl::ParameterSet>("ClusterAlg"));
130 
131  m_timeToProcess = 0.;
132 
133  return;
134 }
135 
136 void ClusterPathFinder::initializeHistograms(art::TFileDirectory&)
137 {
138  return;
139 }
140 
142 {
143  /**
144  * @brief Top level interface for algorithm to consider pairs of clusters from the input
145  * list and determine if they are consistent with each other and, therefore, should
146  * be merged. This is done by looking at the PCA for each cluster and looking at the
147  * projection of the primary axis along the vector connecting their centers.
148  */
149 
150  // Initial clustering is done, now trim the list and get output parameters
151  cet::cpu_timer theClockBuildClusters;
152 
153  // Start clocks if requested
154  if (m_enableMonitoring) theClockBuildClusters.start();
155 
156  int countClusters(0);
157 
158  // This is the loop over candidate 3D clusters
159  // Note that it might be that the list of candidate clusters is modified by splitting
160  // So we use the following construct to make sure we get all of them
161  reco::ClusterParametersList::iterator clusterParametersListItr = clusterParametersList.begin();
162 
163  while(clusterParametersListItr != clusterParametersList.end())
164  {
165  // Dereference to get the cluster paramters
166  reco::ClusterParameters& clusterParameters = *clusterParametersListItr;
167 
168  std::cout << "**> Looking at Cluster " << countClusters++ << std::endl;
169 
170  // It turns out that computing the convex hull surrounding the points in the 2D projection onto the
171  // plane of largest spread in the PCA is a good way to break up the cluster... and we do it here since
172  // we (currently) want this to be part of the standard output
173  buildVoronoiDiagram(clusterParameters);
174 
175  // Make sure our cluster has enough hits...
176  if (clusterParameters.getHitPairListPtr().size() > m_minTinyClusterSize)
177  {
178  // Get an interim cluster list
179  reco::ClusterParametersList reclusteredParameters;
180 
181  // Call the main workhorse algorithm for building the local version of candidate 3D clusters
182  m_clusterAlg->Cluster3DHits(clusterParameters.getHitPairListPtr(), reclusteredParameters);
183 
184  std::cout << ">>>>>>>>>>> Reclustered to " << reclusteredParameters.size() << " Clusters <<<<<<<<<<<<<<<" << std::endl;
185 
186  // Only process non-empty results
187  if (!reclusteredParameters.empty())
188  {
189  // Loop over the reclustered set
190  for (auto& cluster : reclusteredParameters)
191  {
192  std::cout << "****> Calling breakIntoTinyBits" << std::endl;
193 
194  // Break our cluster into smaller elements...
195  breakIntoTinyBits(cluster, cluster.daughterList().end(), cluster.daughterList(), 4);
196 
197  std::cout << "****> Broke Cluster with " << cluster.getHitPairListPtr().size() << " into " << cluster.daughterList().size() << " sub clusters";
198  for(auto& clus : cluster.daughterList()) std::cout << ", " << clus.getHitPairListPtr().size();
199  std::cout << std::endl;
200 
201  // Add the daughters to the cluster
202  clusterParameters.daughterList().insert(clusterParameters.daughterList().end(),cluster);
203  }
204  }
205  }
206 
207  // Go to next cluster parameters object
208  clusterParametersListItr++;
209  }
210 
211  if (m_enableMonitoring)
212  {
213  theClockBuildClusters.stop();
214 
215  m_timeToProcess = theClockBuildClusters.accumulated_real_time();
216  }
217 
218  mf::LogDebug("Cluster3D") << ">>>>> Cluster Path finding done" << std::endl;
219 
220  return;
221 }
222 
225  reco::ClusterParametersList& outputClusterList,
226  int level) const
227 {
228  // This needs to be a recursive routine...
229  // Idea is to take the input cluster and order 3D hits by arclength along PCA primary axis
230  // If the cluster is above the minimum number of hits then we divide into two and call ourself
231  // with the two halves. This means we form a new cluster with hits and PCA and then call ourself
232  // If the cluster is below the minimum then we can't break any more, simply add this cluster to
233  // the new output list.
234 
235  // set an indention string
236  std::string pluses(level/2, '+');
237  std::string indent(level/2, ' ');
238 
239  indent += pluses;
240 
241  reco::ClusterParametersList::iterator inputPositionItr = positionItr;
242 
243  // To make best use of this we'll also want the PCA for this cluster... so...
244  // Recover the prime ingredients
245  reco::PrincipalComponents& fullPCA = clusterToBreak.getFullPCA();
246 
247  std::cout << indent << ">>> breakIntoTinyBits with " << clusterToBreak.getHitPairListPtr().size() << " input hits " << std::endl;
248 
249  // It turns out that computing the convex hull surrounding the points in the 2D projection onto the
250  // plane of largest spread in the PCA is a good way to break up the cluster... and we do it here since
251  // we (currently) want this to be part of the standard output
252  buildConvexHull(clusterToBreak, level+2);
253 
254  bool storeCurrentCluster(true);
255  int minimumClusterSize(m_minTinyClusterSize);
256 
257  // Create a rough cut intended to tell us when we've reached the land of diminishing returns
258  if (clusterToBreak.getBestEdgeList().size() > 6 &&
259  clusterToBreak.getHitPairListPtr().size() > size_t(2 * minimumClusterSize))
260  {
261  // Recover the list of 3D hits associated to this cluster
262  reco::HitPairListPtr& clusHitPairVector = clusterToBreak.getHitPairListPtr();
263 
264  // Calculate the doca to the PCA primary axis for each 3D hit
265  // Importantly, this also gives us the arclength along that axis to the hit
266  m_pcaAlg.PCAAnalysis_calc3DDocas(clusHitPairVector, fullPCA);
267 
268  // Sort the hits along the PCA
269  clusHitPairVector.sort([](const auto& left, const auto& right){return left->getArclenToPoca() < right->getArclenToPoca();});
270 
271  // Now we use the convex hull vertex points to form split points for breaking up the incoming cluster
272  reco::EdgeList& bestEdgeList = clusterToBreak.getBestEdgeList();
273  std::vector<const reco::ClusterHit3D*> vertexHitVec;
274 
275  std::cout << indent << "+> Breaking cluster, convex hull has " << bestEdgeList.size() << " edges to work with" << std::endl;
276 
277  for(const auto& edge : bestEdgeList)
278  {
279  vertexHitVec.push_back(std::get<0>(edge));
280  vertexHitVec.push_back(std::get<1>(edge));
281  }
282 
283  // Sort this vector, we aren't worried about duplicates right now...
284  std::sort(vertexHitVec.begin(),vertexHitVec.end(),[](const auto& left, const auto& right){return left->getArclenToPoca() < right->getArclenToPoca();});
285 
286  // Now we create a list of pairs of iterators to the start and end of each subcluster
287  using Hit3DItrPair = std::pair<reco::HitPairListPtr::iterator,reco::HitPairListPtr::iterator>;
288  using VertexPairList = std::list<Hit3DItrPair>;
289 
290  VertexPairList vertexPairList;
291  reco::HitPairListPtr::iterator firstHitItr = clusHitPairVector.begin();
292 
293  for(const auto& hit3D : vertexHitVec)
294  {
295  reco::HitPairListPtr::iterator vertexItr = std::find(firstHitItr,clusHitPairVector.end(),hit3D);
296 
297  if (vertexItr == clusHitPairVector.end())
298  {
299  std::cout << indent << ">>>>>>>>>>>>>>>>> Hit not found in input list, cannot happen? <<<<<<<<<<<<<<<<<<<" << std::endl;
300  break;
301  }
302 
303  std::cout << indent << "+> -- Distance from first to current vertex point: " << std::distance(firstHitItr,vertexItr) << " first: " << *firstHitItr << ", vertex: " << *vertexItr;
304 
305  // Require a minimum number of points...
306  if (std::distance(firstHitItr,vertexItr) > minimumClusterSize)
307  {
308  vertexPairList.emplace_back(Hit3DItrPair(firstHitItr,vertexItr));
309  firstHitItr = vertexItr;
310 
311  std::cout << " ++ made pair ";
312  }
313 
314  std::cout << std::endl;
315  }
316 
317  // Not done if there is distance from first to end of list
318  if (std::distance(firstHitItr,clusHitPairVector.end()) > 0)
319  {
320  std::cout << indent << "+> loop over vertices done, remant distance: " << std::distance(firstHitItr,clusHitPairVector.end()) << std::endl;
321 
322  // In the event we don't have the minimum number of hits we simply extend the last pair
323  if (!vertexPairList.empty() && std::distance(firstHitItr,clusHitPairVector.end()) < minimumClusterSize)
324  vertexPairList.back().second = clusHitPairVector.end();
325  else
326  vertexPairList.emplace_back(Hit3DItrPair(firstHitItr,clusHitPairVector.end()));
327  }
328 
329  std::cout << indent << "+> ---> breaking cluster into " << vertexPairList.size() << " subclusters" << std::endl;
330 
331  if (vertexPairList.size() > 1)
332  {
333  storeCurrentCluster = false;
334 
335  // Ok, now loop through our pairs
336  for(auto& hit3DItrPair : vertexPairList)
337  {
338  reco::ClusterParameters clusterParams;
339  reco::HitPairListPtr& hitPairListPtr = clusterParams.getHitPairListPtr();
340 
341  std::cout << indent << "+> -- building new cluster, size: " << std::distance(hit3DItrPair.first,hit3DItrPair.second) << std::endl;
342 
343  // size the container...
344  hitPairListPtr.resize(std::distance(hit3DItrPair.first,hit3DItrPair.second));
345 
346  // and copy the hits into the container
347  std::copy(hit3DItrPair.first,hit3DItrPair.second,hitPairListPtr.begin());
348 
349  // First stage of feature extraction runs here
350  m_pcaAlg.PCAAnalysis_3D(hitPairListPtr, clusterParams.getFullPCA());
351 
352  // Recover the new fullPCA
353  reco::PrincipalComponents& newFullPCA = clusterParams.getFullPCA();
354 
355  // Must have a valid pca
356  if (newFullPCA.getSvdOK())
357  {
358  std::cout << indent << "+> -- >> cluster has a valid Full PCA" << std::endl;
359 
360  // Need to check if the PCA direction has been reversed
361  Eigen::Vector3f fullPrimaryVec(fullPCA.getEigenVectors().row(0));
362  Eigen::Vector3f newPrimaryVec(newFullPCA.getEigenVectors().row(0));
363 
364  // If the PCA's are opposite the flip the axes
365  if (fullPrimaryVec.dot(newPrimaryVec) < 0.)
366  {
367  for(size_t vecIdx = 0; vecIdx < 3; vecIdx++) newFullPCA.flipAxis(vecIdx);
368  }
369 
370  // Set the skeleton PCA to make sure it has some value
371  clusterParams.getSkeletonPCA() = clusterParams.getFullPCA();
372 
373  positionItr = breakIntoTinyBits(clusterParams, positionItr, outputClusterList, level+4);
374  }
375  }
376  }
377  }
378 
379  // First question, are we done breaking?
380  if (storeCurrentCluster)
381  {
382  // I think this is where we fill out the rest of the parameters?
383  // Start by adding the 2D hits...
384  // See if we can avoid duplicates by temporarily transferring to a set
385  std::set<const reco::ClusterHit2D*> hitSet;
386 
387  // Loop through 3D hits to get a set of unique 2D hits
388  for(const auto& hit3D : clusterToBreak.getHitPairListPtr())
389  {
390  for(const auto& hit2D : hit3D->getHits())
391  {
392  if (hit2D) hitSet.insert(hit2D);
393  }
394  }
395 
396  // Now add these to the new cluster
397  for(const auto& hit2D : hitSet)
398  {
399  hit2D->setStatusBit(reco::ClusterHit2D::USED);
400  clusterToBreak.UpdateParameters(hit2D);
401  }
402 
403  std::cout << indent << "*********>>> storing new subcluster of size " << clusterToBreak.getHitPairListPtr().size() << std::endl;
404 
405  positionItr = outputClusterList.insert(positionItr,clusterToBreak);
406 
407  // The above points to the element, want the next element
408  positionItr++;
409  }
410  else if (inputPositionItr == positionItr) std::cout << indent << "***** DID NOT STORE A CLUSTER *****" << std::endl;
411 
412  return positionItr;
413 }
414 
416 {
417  // set an indention string
418  std::string minuses(level/2, '-');
419  std::string indent(level/2, ' ');
420 
421  indent += minuses;
422 
423  // The plan is to build the enclosing 2D polygon around the points in the PCA plane of most spread for this cluster
424  // To do so we need to start by building a list of 2D projections onto the plane of most spread...
425  reco::PrincipalComponents& pca = clusterParameters.getFullPCA();
426 
427  // Recover the parameters from the Principal Components Analysis that we need to project and accumulate
428  const Eigen::Vector3f& pcaCenter = pca.getAvePosition();
429 
430  //dcel2d::PointList pointList;
431  using Point = std::tuple<float,float,const reco::ClusterHit3D*>;
432  using PointList = std::list<Point>;
433  PointList pointList;
434 
435  // Loop through hits and do projection to plane
436  for(const auto& hit3D : clusterParameters.getHitPairListPtr())
437  {
438  Eigen::Vector3f pcaToHitVec(hit3D->getPosition()[0] - pcaCenter(0),
439  hit3D->getPosition()[1] - pcaCenter(1),
440  hit3D->getPosition()[2] - pcaCenter(2));
441  Eigen::Vector3f pcaToHit = pca.getEigenVectors() * pcaToHitVec;
442 
443  pointList.emplace_back(dcel2d::Point(pcaToHit(1),pcaToHit(2),hit3D));
444  }
445 
446  // Sort the point vec by increasing x, then increase y
447  pointList.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);});
448 
449  // containers for finding the "best" hull...
450  std::vector<ConvexHull> convexHullVec;
451  std::vector<PointList> rejectedListVec;
452  bool increaseDepth(pointList.size() > 5);
453  float lastArea(std::numeric_limits<float>::max());
454 
455  while(increaseDepth)
456  {
457  // Get another convexHull container
458  convexHullVec.push_back(ConvexHull(pointList));
459  rejectedListVec.push_back(PointList());
460 
461  const ConvexHull& convexHull = convexHullVec.back();
462  PointList& rejectedList = rejectedListVec.back();
463  const PointList& convexHullPoints = convexHull.getConvexHull();
464 
465  increaseDepth = false;
466 
467  if (convexHull.getConvexHullArea() > 0.)
468  {
469  std::cout << indent << "-> built convex hull, 3D hits: " << pointList.size() << " with " << convexHullPoints.size() << " vertices" << ", area: " << convexHull.getConvexHullArea() << std::endl;
470  std::cout << indent << "-> -Points:";
471  for(const auto& point : convexHullPoints)
472  std::cout << " (" << std::get<0>(point) << "," << std::get<1>(point) << ")";
473  std::cout << std::endl;
474 
475  if (convexHullVec.size() < 2 || convexHull.getConvexHullArea() < 0.8 * lastArea)
476  {
477  for(auto& point : convexHullPoints)
478  {
479  pointList.remove(point);
480  rejectedList.emplace_back(point);
481  }
482  lastArea = convexHull.getConvexHullArea();
483 // increaseDepth = true;
484  }
485  }
486  }
487 
488  // do we have a valid convexHull?
489  while(!convexHullVec.empty() && convexHullVec.back().getConvexHullArea() < 0.5)
490  {
491  convexHullVec.pop_back();
492  rejectedListVec.pop_back();
493  }
494 
495  // If we found the convex hull then build edges around the region
496  if (!convexHullVec.empty())
497  {
498  size_t nRejectedTotal(0);
499  reco::HitPairListPtr hitPairListPtr = clusterParameters.getHitPairListPtr();
500 
501  for(const auto& rejectedList : rejectedListVec)
502  {
503  nRejectedTotal += rejectedList.size();
504 
505  for(const auto& rejectedPoint : rejectedList)
506  {
507  std::cout << indent << "-> -- Point is " << convexHullVec.back().findNearestDistance(rejectedPoint) << " from nearest edge" << std::endl;
508 
509  if (convexHullVec.back().findNearestDistance(rejectedPoint) > 0.5)
510  hitPairListPtr.remove(std::get<2>(rejectedPoint));
511  }
512  }
513 
514  std::cout << indent << "-> Removed " << nRejectedTotal << " leaving " << pointList.size() << "/" << hitPairListPtr.size() << " points" << std::endl;
515 
516  // Now add "edges" to the cluster to describe the convex hull (for the display)
517  reco::Hit3DToEdgeMap& edgeMap = clusterParameters.getHit3DToEdgeMap();
518  reco::EdgeList& bestEdgeList = clusterParameters.getBestEdgeList();
519 
520  Point lastPoint = convexHullVec.back().getConvexHull().front();
521 
522  for(auto& curPoint : convexHullVec.back().getConvexHull())
523  {
524  if (curPoint == lastPoint) continue;
525 
526  const reco::ClusterHit3D* lastPoint3D = std::get<2>(lastPoint);
527  const reco::ClusterHit3D* curPoint3D = std::get<2>(curPoint);
528 
529  float distBetweenPoints = (curPoint3D->getPosition()[0] - lastPoint3D->getPosition()[0]) * (curPoint3D->getPosition()[0] - lastPoint3D->getPosition()[0])
530  + (curPoint3D->getPosition()[1] - lastPoint3D->getPosition()[1]) * (curPoint3D->getPosition()[1] - lastPoint3D->getPosition()[1])
531  + (curPoint3D->getPosition()[2] - lastPoint3D->getPosition()[2]) * (curPoint3D->getPosition()[2] - lastPoint3D->getPosition()[2]);
532 
533  distBetweenPoints = std::sqrt(distBetweenPoints);
534 
535  reco::EdgeTuple edge(lastPoint3D,curPoint3D,distBetweenPoints);
536 
537  edgeMap[lastPoint3D].push_back(edge);
538  edgeMap[curPoint3D].push_back(edge);
539  bestEdgeList.emplace_back(edge);
540 
541  lastPoint = curPoint;
542  }
543  }
544 
545  return;
546 }
547 
548 
550 {
551  // The plan is to build the enclosing 2D polygon around the points in the PCA plane of most spread for this cluster
552  // To do so we need to start by building a list of 2D projections onto the plane of most spread...
553  reco::PrincipalComponents& pca = clusterParameters.getFullPCA();
554 
555  // Recover the parameters from the Principal Components Analysis that we need to project and accumulate
556  const Eigen::Vector3f& pcaCenter = pca.getAvePosition();
557  dcel2d::PointList pointList;
558 
559  // Loop through hits and do projection to plane
560  for(const auto& hit3D : clusterParameters.getHitPairListPtr())
561  {
562  Eigen::Vector3f pcaToHitVec(hit3D->getPosition()[0] - pcaCenter(0),
563  hit3D->getPosition()[1] - pcaCenter(1),
564  hit3D->getPosition()[2] - pcaCenter(2));
565  Eigen::Vector3f pcaToHit = pca.getEigenVectors() * pcaToHitVec;
566 
567  pointList.emplace_back(dcel2d::Point(pcaToHit(1),pcaToHit(2),hit3D));
568  }
569 
570  // Sort the point vec by increasing x, then increase y
571  pointList.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);});
572 
573  // Set up the voronoi diagram builder
574  voronoi2d::VoronoiDiagram voronoiDiagram(clusterParameters.getHalfEdgeList(),clusterParameters.getVertexList(),clusterParameters.getFaceList());
575 
576  // And make the diagram
577  voronoiDiagram.buildVoronoiDiagram(pointList);
578 
579  // Recover the voronoi diagram vertex list and the container to store them in
580  dcel2d::VertexList& vertexList = clusterParameters.getVertexList();
581 
582  // Now get the inverse of the rotation matrix so we can get the vertex positions,
583  // which lie in the plane of the two largest PCA axes, in the standard coordinate system
584  Eigen::Matrix3f rotationMatrixInv = pca.getEigenVectors().inverse();
585 
586  // Translate and fill
587  for(auto& vertex : vertexList)
588  {
589  Eigen::Vector3f coords = rotationMatrixInv * vertex.getCoords();
590 
591  coords += pcaCenter;
592 
593  vertex.setCoords(coords);
594  }
595 
596  // Now do the Convex Hull
597  // Now add "edges" to the cluster to describe the convex hull (for the display)
598  reco::Hit3DToEdgeMap& edgeMap = clusterParameters.getHit3DToEdgeMap();
599  reco::EdgeList& bestEdgeList = clusterParameters.getBestEdgeList();
600 
601 // const dcel2d::PointList& edgePoints = voronoiDiagram.getConvexHull();
602 // PointList localList;
603 //
604 // for(const auto& edgePoint : edgePoints) localList.emplace_back(std::get<0>(edgePoint),std::get<1>(edgePoint),std::get<2>(edgePoint));
605 //
606 // // Sort the point vec by increasing x, then increase y
607 // 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);});
608 //
609 // // Why do I need to do this?
610 // ConvexHull convexHull(localList);
611 //
612 // Point lastPoint = convexHull.getConvexHull().front();
613  dcel2d::Point lastPoint = voronoiDiagram.getConvexHull().front();
614 
615 // std::cout << "@@@@>> Build convex hull, voronoi handed " << edgePoints.size() << " points, convexHull cut to " << convexHull.getConvexHull().size() << std::endl;
616 
617 // for(auto& curPoint : convexHull.getConvexHull())
618  for(auto& curPoint : voronoiDiagram.getConvexHull())
619  {
620  if (curPoint == lastPoint) continue;
621 
622  const reco::ClusterHit3D* lastPoint3D = std::get<2>(lastPoint);
623  const reco::ClusterHit3D* curPoint3D = std::get<2>(curPoint);
624 
625  float distBetweenPoints = (curPoint3D->getPosition()[0] - lastPoint3D->getPosition()[0]) * (curPoint3D->getPosition()[0] - lastPoint3D->getPosition()[0])
626  + (curPoint3D->getPosition()[1] - lastPoint3D->getPosition()[1]) * (curPoint3D->getPosition()[1] - lastPoint3D->getPosition()[1])
627  + (curPoint3D->getPosition()[2] - lastPoint3D->getPosition()[2]) * (curPoint3D->getPosition()[2] - lastPoint3D->getPosition()[2]);
628 
629  distBetweenPoints = std::sqrt(distBetweenPoints);
630 
631  reco::EdgeTuple edge(lastPoint3D,curPoint3D,distBetweenPoints);
632 
633  edgeMap[lastPoint3D].push_back(edge);
634  edgeMap[curPoint3D].push_back(edge);
635  bestEdgeList.emplace_back(edge);
636 
637  lastPoint = curPoint;
638  }
639 
640  std::cout << "****> vertexList containted " << vertexList.size() << " vertices" << std::endl;
641 
642  return;
643 }
644 
645 
646 float ClusterPathFinder::closestApproach(const Eigen::Vector3f& P0,
647  const Eigen::Vector3f& u0,
648  const Eigen::Vector3f& P1,
649  const Eigen::Vector3f& u1,
650  Eigen::Vector3f& poca0,
651  Eigen::Vector3f& poca1) const
652 {
653  // Technique is to compute the arclength to each point of closest approach
654  Eigen::Vector3f w0 = P0 - P1;
655  float a(1.);
656  float b(u0.dot(u1));
657  float c(1.);
658  float d(u0.dot(w0));
659  float e(u1.dot(w0));
660  float den(a * c - b * b);
661 
662  float arcLen0 = (b * e - c * d) / den;
663  float arcLen1 = (a * e - b * d) / den;
664 
665  poca0 = P0 + arcLen0 * u0;
666  poca1 = P1 + arcLen1 * u1;
667 
668  return (poca0 - poca1).norm();
669 }
670 
672 } // namespace lar_cluster3d
intermediate_table::iterator iterator
void PCAAnalysis_calc3DDocas(const reco::HitPairListPtr &hitPairVector, const reco::PrincipalComponents &pca) const
bool getSvdOK() const
Definition: Cluster3D.h:244
#define DEFINE_ART_CLASS_TOOL(tool)
Definition: ToolMacros.h:42
void PCAAnalysis_3D(const reco::HitPairListPtr &hitPairList, reco::PrincipalComponents &pca, bool skeletonOnly=false) const
void cluster(In first, In last, Out result, Pred *pred)
Definition: NNClusters.h:41
void flipAxis(size_t axis)
Definition: Cluster3D.cxx:208
std::string string
Definition: nybbler.cc:12
const Eigen::Vector3f getPosition() const
Definition: Cluster3D.h:158
void configure(fhicl::ParameterSet const &pset) override
reco::PrincipalComponents & getSkeletonPCA()
Definition: Cluster3D.h:478
std::unique_ptr< lar_cluster3d::IClusterAlg > m_clusterAlg
Algorithm to do 3D space point clustering.
reco::EdgeList & getBestEdgeList()
Definition: Cluster3D.h:481
float closestApproach(const Eigen::Vector3f &, const Eigen::Vector3f &, const Eigen::Vector3f &, const Eigen::Vector3f &, Eigen::Vector3f &, Eigen::Vector3f &) const
reco::Hit3DToEdgeMap & getHit3DToEdgeMap()
Definition: Cluster3D.h:479
reco::HitPairListPtr & getHitPairListPtr()
Definition: Cluster3D.h:476
Cluster finding and building.
std::pair< MinMaxPoints, MinMaxPoints > MinMaxPointPair
float getTimeToExecute() const override
If monitoring, recover the time to execute a particular function.
IClusterModAlg interface class definiton.
ClusterParametersList & daughterList()
Definition: Cluster3D.h:450
Implements a ConvexHull for use in clustering.
T abs(T value)
std::list< EdgeTuple > EdgeList
Definition: Cluster3D.h:345
void buildConvexHull(reco::ClusterParameters &clusterParameters, int level=0) const
void initializeHistograms(art::TFileDirectory &) override
Interface for initializing histograms if they are desired Note that the idea is to put hisgtograms in...
const double e
VoronoiDiagram class definiton.
Definition: Voronoi.h:31
reco::PrincipalComponents & getFullPCA()
Definition: Cluster3D.h:477
std::tuple< double, double, const reco::ClusterHit3D * > Point
Definitions used by the VoronoiDiagram algorithm.
Definition: DCEL.h:44
const double a
void buildVoronoiDiagram(const dcel2d::PointList &)
Given an input set of 2D points construct a 2D voronoi diagram.
Definition: Voronoi.cxx:133
T get(std::string const &key) const
Definition: ParameterSet.h:271
const Eigen::Vector3f & getAvePosition() const
Definition: Cluster3D.h:248
size_t m_minTinyClusterSize
Minimum size for a "tiny" cluster.
std::tuple< const reco::ClusterHit3D *, const reco::ClusterHit3D *, double > EdgeTuple
Definition: Cluster3D.h:344
std::list< const reco::ClusterHit3D * > HitPairListPtr
Definition: Cluster3D.h:335
void UpdateParameters(const reco::ClusterHit2D *hit)
Definition: Cluster3D.h:452
float getConvexHullArea() const
recover the area of the convex hull
Definition: ConvexHull.h:78
This provides an art tool interface definition for 3D Cluster algorithms.
double distance(double x1, double y1, double z1, double x2, double y2, double z2)
static int max(int a, int b)
const PointList & getConvexHull() const
recover the list of convex hull vertices
Definition: ConvexHull.h:58
reco::ClusterParametersList::iterator breakIntoTinyBits(reco::ClusterParameters &cluster, reco::ClusterParametersList::iterator positionItr, reco::ClusterParametersList &outputClusterList, int level=0) const
Use PCA to try to find path in cluster.
std::pair< Point, Point > MinMaxPoints
ConvexHull class definiton.
Definition: ConvexHull.h:27
bool m_enableMonitoring
Data members to follow.
void buildVoronoiDiagram(reco::ClusterParameters &clusterParameters, int level=0) const
dcel2d::FaceList & getFaceList()
Definition: Cluster3D.h:483
MaybeLogger_< ELseverityLevel::ELsev_success, false > LogDebug
This provides an art tool interface definition for 3D Cluster algorithms.
void ModifyClusters(reco::ClusterParametersList &) const override
Scan an input collection of clusters and modify those according to the specific implementing algorith...
T copy(T const &v)
static bool * b
Definition: config.cpp:1043
std::list< Point > PointList
Definition: DCEL.h:45
std::unordered_map< const reco::ClusterHit3D *, reco::EdgeList > Hit3DToEdgeMap
Definition: Cluster3D.h:347
double accumulated_real_time() const
Definition: cpu_timer.cc:66
auto const & get(AssnsNode< L, R, D > const &r)
Definition: AssnsNode.h:115
ClusterPathFinder(const fhicl::ParameterSet &)
Constructor.
void start()
Definition: cpu_timer.cc:83
dcel2d::HalfEdgeList & getHalfEdgeList()
Definition: Cluster3D.h:485
std::list< Vertex > VertexList
Definition: DCEL.h:182
std::list< ClusterParameters > ClusterParametersList
Definition: Cluster3D.h:404
const EigenVectors & getEigenVectors() const
Definition: Cluster3D.h:247
QTextStream & endl(QTextStream &s)
dcel2d::VertexList & getVertexList()
Definition: Cluster3D.h:484
vertex reconstruction