VoronoiPathFinder_tool.cc
Go to the documentation of this file.
1 /**
2  * @file VoronoiPathFinder_tool.cc
3  *
4  * @brief art Tool for comparing clusters and merging those that are consistent
5  *
6  */
7 
8 // Framework Includes
11 #include "art_root_io/TFileDirectory.h"
12 #include "cetlib/cpu_timer.h"
14 
18 
19 // LArSoft includes
22 
23 // Eigen
24 #include <Eigen/Core>
25 
26 // Root histograms
27 #include "TH1F.h"
28 
29 // std includes
30 #include <string>
31 #include <iostream>
32 #include <memory>
33 
34 //------------------------------------------------------------------------------------------------------------------------------------------
35 // implementation follows
36 
37 namespace lar_cluster3d {
38 
39 class VoronoiPathFinder : virtual public IClusterModAlg
40 {
41 public:
42  /**
43  * @brief Constructor
44  *
45  * @param pset
46  */
47  explicit VoronoiPathFinder(const fhicl::ParameterSet&);
48 
49  /**
50  * @brief Destructor
51  */
53 
54  void configure(fhicl::ParameterSet const &pset) override;
55 
56  /**
57  * @brief Interface for initializing histograms if they are desired
58  * Note that the idea is to put hisgtograms in a subfolder
59  *
60  * @param TFileDirectory - the folder to store the hists in
61  */
62  void initializeHistograms(art::TFileDirectory&) override;
63 
64  /**
65  * @brief Scan an input collection of clusters and modify those according
66  * to the specific implementing algorithm
67  *
68  * @param clusterParametersList A list of cluster objects (parameters from associated hits)
69  */
70  void ModifyClusters(reco::ClusterParametersList&) const override;
71 
72  /**
73  * @brief If monitoring, recover the time to execute a particular function
74  */
75  float getTimeToExecute() const override {return fTimeToProcess;}
76 
77 private:
78 
79  /**
80  * @brief Use PCA to try to find path in cluster
81  *
82  * @param clusterParameters The given cluster parameters object to try to split
83  * @param clusterParametersList The list of clusters
84  */
88  reco::ClusterParametersList& outputClusterList,
89  int level = 0) const;
90 
91  /**
92  * @brief Use PCA to try to find path in cluster
93  *
94  * @param clusterParameters The given cluster parameters object to try to split
95  * @param clusterParametersList The list of clusters
96  */
100  reco::ClusterParametersList& outputClusterList,
101  int level = 0) const;
102 
103  bool makeCandidateCluster(Eigen::Vector3f&,
107  int) const;
108 
109  float closestApproach(const Eigen::Vector3f&,
110  const Eigen::Vector3f&,
111  const Eigen::Vector3f&,
112  const Eigen::Vector3f&,
113  Eigen::Vector3f&,
114  Eigen::Vector3f&) const;
115 
116  using Point = std::tuple<float,float,const reco::ClusterHit3D*>;
117  using PointList = std::list<Point>;
118  using MinMaxPoints = std::pair<Point,Point>;
119  using MinMaxPointPair = std::pair<MinMaxPoints,MinMaxPoints>;
120 
121  void buildConvexHull(reco::ClusterParameters& clusterParameters, int level = 0) const;
122  void buildVoronoiDiagram(reco::ClusterParameters& clusterParameters, int level = 0) const;
123 
125  /**
126  * @brief FHICL parameters
127  */
128  bool fEnableMonitoring; ///<
129  size_t fMinTinyClusterSize; ///< Minimum size for a "tiny" cluster
130  mutable float fTimeToProcess; ///<
131 
132  /**
133  * @brief Histogram definitions
134  */
136 
143 
154 
155  /**
156  * @brief Tools
157  */
158  std::unique_ptr<lar_cluster3d::IClusterAlg> fClusterAlg; ///< Algorithm to do 3D space point clustering
159  PrincipalComponentsAlg fPCAAlg; // For running Principal Components Analysis
160 };
161 
163  fPCAAlg(pset.get<fhicl::ParameterSet>("PrincipalComponentsAlg"))
164 {
165  this->configure(pset);
166 }
167 
168 //------------------------------------------------------------------------------------------------------------------------------------------
169 
171 {
172 }
173 
174 //------------------------------------------------------------------------------------------------------------------------------------------
175 
177 {
178  fEnableMonitoring = pset.get<bool> ("EnableMonitoring", true );
179  fMinTinyClusterSize = pset.get<size_t>("MinTinyClusterSize",40);
180  fClusterAlg = art::make_tool<lar_cluster3d::IClusterAlg>(pset.get<fhicl::ParameterSet>("ClusterAlg"));
181 
182  fTimeToProcess = 0.;
183 
184  return;
185 }
186 
187 void VoronoiPathFinder::initializeHistograms(art::TFileDirectory& histDir)
188 {
189  // It is assumed that the input TFileDirectory has been set up to group histograms into a common
190  // folder at the calling routine's level. Here we create one more level of indirection to keep
191  // histograms made by this tool separate.
192  fFillHistograms = true;
193 
194  std::string dirName = "VoronoiPath";
195 
196  art::TFileDirectory dir = histDir.mkdir(dirName.c_str());
197 
198  // Divide into two sets of hists... those for the overall cluster and
199  // those for the subclusters
200  fTopNum3DHits = dir.make<TH1F>("TopNum3DHits", "Number 3D Hits", 200, 0., 200.);
201  fTopNumEdges = dir.make<TH1F>("TopNumEdges", "Number Edges", 200, 0., 200.);
202  fTopEigen21Ratio = dir.make<TH1F>("TopEigen21Rat", "Eigen 2/1 Ratio", 100, 0., 1.);
203  fTopEigen20Ratio = dir.make<TH1F>("TopEigen20Rat", "Eigen 2/0 Ratio", 100, 0., 1.);
204  fTopEigen10Ratio = dir.make<TH1F>("TopEigen10Rat", "Eigen 1/0 Ratio", 100, 0., 1.);
205  fTopPrimaryLength = dir.make<TH1F>("TopPrimaryLen", "Primary Length", 200, 0., 200.);
206 
207  fSubNum3DHits = dir.make<TH1F>("SubNum3DHits", "Number 3D Hits", 200, 0., 200.);
208  fSubNumEdges = dir.make<TH1F>("SubNumEdges", "Number Edges", 200, 0., 200.);
209  fSubEigen21Ratio = dir.make<TH1F>("SubEigen21Rat", "Eigen 2/1 Ratio", 100, 0., 1.);
210  fSubEigen20Ratio = dir.make<TH1F>("SubEigen20Rat", "Eigen 2/0 Ratio", 100, 0., 1.);
211  fSubEigen10Ratio = dir.make<TH1F>("SubEigen10Rat", "Eigen 1/0 Ratio", 100, 0., 1.);
212  fSubPrimaryLength = dir.make<TH1F>("SubPrimaryLen", "Primary Length", 200, 0., 200.);
213  fSubCosToPrevPCA = dir.make<TH1F>("SubCosToPrev", "Cos(theta)", 101, 0., 1.01);
214  fSubCosExtToPCA = dir.make<TH1F>("SubCosExtPCA", "Cos(theta)", 102, -1.01, 1.01);
215  fSubMaxDefect = dir.make<TH1F>("SubMaxDefect", "Max Defect", 100, 0., 50.);
216  fSubUsedDefect = dir.make<TH1F>("SubUsedDefect", "Used Defect", 100, 0., 50.);
217 
218  return;
219 }
220 
222 {
223  /**
224  * @brief Top level interface for algorithm to consider pairs of clusters from the input
225  * list and determine if they are consistent with each other and, therefore, should
226  * be merged. This is done by looking at the PCA for each cluster and looking at the
227  * projection of the primary axis along the vector connecting their centers.
228  */
229 
230  // Initial clustering is done, now trim the list and get output parameters
231  cet::cpu_timer theClockBuildClusters;
232 
233  // Start clocks if requested
234  if (fEnableMonitoring) theClockBuildClusters.start();
235 
236  int countClusters(0);
237 
238  // This is the loop over candidate 3D clusters
239  // Note that it might be that the list of candidate clusters is modified by splitting
240  // So we use the following construct to make sure we get all of them
241  reco::ClusterParametersList::iterator clusterParametersListItr = clusterParametersList.begin();
242 
243  while(clusterParametersListItr != clusterParametersList.end())
244  {
245  // Dereference to get the cluster paramters
246  reco::ClusterParameters& clusterParameters = *clusterParametersListItr;
247 
248  std::cout << "**> Looking at Cluster " << countClusters++ << ", # hits: " << clusterParameters.getHitPairListPtr().size() << std::endl;
249 
250  // It turns out that computing the convex hull surrounding the points in the 2D projection onto the
251  // plane of largest spread in the PCA is a good way to break up the cluster... and we do it here since
252  // we (currently) want this to be part of the standard output
253  buildVoronoiDiagram(clusterParameters);
254 
255  // Make sure our cluster has enough hits...
256  if (clusterParameters.getHitPairListPtr().size() > fMinTinyClusterSize)
257  {
258  // Get an interim cluster list
259  reco::ClusterParametersList reclusteredParameters;
260 
261  // Call the main workhorse algorithm for building the local version of candidate 3D clusters
262  //******** Remind me why we need to call this at this point when the same hits will be used? ********
263  //fClusterAlg->Cluster3DHits(clusterParameters.getHitPairListPtr(), reclusteredParameters);
264  reclusteredParameters.push_back(clusterParameters);
265 
266  std::cout << ">>>>>>>>>>> Reclustered to " << reclusteredParameters.size() << " Clusters <<<<<<<<<<<<<<<" << std::endl;
267 
268  // Only process non-empty results
269  if (!reclusteredParameters.empty())
270  {
271  // Loop over the reclustered set
272  for (auto& cluster : reclusteredParameters)
273  {
274  std::cout << "****> Calling breakIntoTinyBits with " << cluster.getHitPairListPtr().size() << " hits" << std::endl;
275 
276  // It turns out that computing the convex hull surrounding the points in the 2D projection onto the
277  // plane of largest spread in the PCA is a good way to break up the cluster... and we do it here since
278  // we (currently) want this to be part of the standard output
280 
281  // Break our cluster into smaller elements...
282  subDivideCluster(cluster, cluster.getFullPCA(), cluster.daughterList().end(), cluster.daughterList(), 4);
283 
284  std::cout << "****> Broke Cluster with " << cluster.getHitPairListPtr().size() << " into " << cluster.daughterList().size() << " sub clusters";
285  for(auto& clus : cluster.daughterList()) std::cout << ", " << clus.getHitPairListPtr().size();
286  std::cout << std::endl;
287 
288  // Add the daughters to the cluster
289  clusterParameters.daughterList().insert(clusterParameters.daughterList().end(),cluster);
290 
291  // If filling histograms we do the main cluster here
292  if (fFillHistograms)
293  {
294  reco::PrincipalComponents& fullPCA = cluster.getFullPCA();
295  std::vector<double> eigenValVec = {3. * std::sqrt(fullPCA.getEigenValues()[0]),
296  3. * std::sqrt(fullPCA.getEigenValues()[1]),
297  3. * std::sqrt(fullPCA.getEigenValues()[2])};
298  double eigen2To1Ratio = eigenValVec[0] / eigenValVec[1];
299  double eigen1To0Ratio = eigenValVec[1] / eigenValVec[2];
300  double eigen2To0Ratio = eigenValVec[0] / eigenValVec[2];
301  int num3DHits = cluster.getHitPairListPtr().size();
302  int numEdges = cluster.getBestEdgeList().size();
303 
304  fTopNum3DHits->Fill(std::min(num3DHits,199), 1.);
305  fTopNumEdges->Fill(std::min(numEdges,199), 1.);
306  fTopEigen21Ratio->Fill(eigen2To1Ratio, 1.);
307  fTopEigen20Ratio->Fill(eigen2To0Ratio, 1.);
308  fTopEigen10Ratio->Fill(eigen1To0Ratio, 1.);
309  fTopPrimaryLength->Fill(std::min(eigenValVec[0],199.), 1.);
310  }
311  }
312  }
313  }
314 
315  // Go to next cluster parameters object
316  clusterParametersListItr++;
317  }
318 
319  if (fEnableMonitoring)
320  {
321  theClockBuildClusters.stop();
322 
323  fTimeToProcess = theClockBuildClusters.accumulated_real_time();
324  }
325 
326  mf::LogDebug("Cluster3D") << ">>>>> Cluster Path finding done" << std::endl;
327 
328  return;
329 }
330 
332  reco::PrincipalComponents& lastPCA,
334  reco::ClusterParametersList& outputClusterList,
335  int level) const
336 {
337  // This needs to be a recursive routine...
338  // Idea is to take the input cluster and order 3D hits by arclength along PCA primary axis
339  // If the cluster is above the minimum number of hits then we divide into two and call ourself
340  // with the two halves. This means we form a new cluster with hits and PCA and then call ourself
341  // If the cluster is below the minimum then we can't break any more, simply add this cluster to
342  // the new output list.
343 
344  // set an indention string
345  std::string pluses(level/2, '+');
346  std::string indent(level/2, ' ');
347 
348  indent += pluses;
349 
350  reco::ClusterParametersList::iterator inputPositionItr = positionItr;
351 
352  // To make best use of this we'll also want the PCA for this cluster... so...
353  // Recover the prime ingredients
354  reco::PrincipalComponents& fullPCA = clusterToBreak.getFullPCA();
355  std::vector<double> eigenValVec = {3. * std::sqrt(fullPCA.getEigenValues()[0]),
356  3. * std::sqrt(fullPCA.getEigenValues()[1]),
357  3. * std::sqrt(fullPCA.getEigenValues()[2])};
358  Eigen::Vector3f fullPrimaryVec(fullPCA.getEigenVectors().row(2));
359  Eigen::Vector3f lastPrimaryVec(lastPCA.getEigenVectors().row(2));
360 
361  double cosNewToLast = std::abs(fullPrimaryVec.dot(lastPrimaryVec));
362  double eigen2To1Ratio = eigenValVec[2] / eigenValVec[1];
363  double eigen1To0Ratio = eigenValVec[1] / eigenValVec[2];
364  double eigen2To0Ratio = eigenValVec[0] / eigenValVec[2];
365  double eigen2And1Ave = 0.5 * (eigenValVec[1] + eigenValVec[0]);
366  double eigenAveTo0Ratio = eigen2And1Ave / eigenValVec[2];
367 
368  bool storeCurrentCluster(true);
369  int minimumClusterSize(fMinTinyClusterSize);
370 
371  std::cout << indent << ">>> breakIntoTinyBits with " << clusterToBreak.getHitPairListPtr().size() << " input hits, " << clusterToBreak.getBestEdgeList().size() << " edges, rat21: " << eigen2To1Ratio << ", rat20: " << eigen2To0Ratio << ", rat10: " << eigen1To0Ratio << ", ave0: " << eigenAveTo0Ratio << std::endl;
372  std::cout << indent << " --> eigen 0/1/2: " << eigenValVec[0] << "/" << eigenValVec[1] << "/" << eigenValVec[2] << ", cos: " << cosNewToLast << std::endl;
373 
374  // Create a rough cut intended to tell us when we've reached the land of diminishing returns
375  if (clusterToBreak.getBestEdgeList().size() > 5 && cosNewToLast > 0.25 && eigen2To1Ratio < 0.9 && eigen2To0Ratio > 0.001 &&
376  clusterToBreak.getHitPairListPtr().size() > size_t(2 * minimumClusterSize))
377  {
378  // We want to find the convex hull vertices that lie furthest from the line to/from the extreme points
379  // To find these we:
380  // 1) recover the extreme points
381  // 2) form the vector between them
382  // 3) loop through the vertices and keep track of distance to this vector
383  // 4) Sort the resulting list by furthest points and select the one we want
384  reco::ProjectedPointList::const_iterator extremePointListItr = clusterToBreak.getConvexHull().getConvexHullExtremePoints().begin();
385 
386  const reco::ClusterHit3D* firstEdgeHit = std::get<2>(*extremePointListItr++);
387  const reco::ClusterHit3D* secondEdgeHit = std::get<2>(*extremePointListItr);
388  Eigen::Vector3f edgeVec(secondEdgeHit->getPosition()[0] - firstEdgeHit->getPosition()[0],
389  secondEdgeHit->getPosition()[1] - firstEdgeHit->getPosition()[1],
390  secondEdgeHit->getPosition()[2] - firstEdgeHit->getPosition()[2]);
391  double edgeLen = edgeVec.norm();
392 
393  // normalize it
394  edgeVec.normalize();
395 
396  // Recover the list of 3D hits associated to this cluster
397  reco::HitPairListPtr& clusHitPairVector = clusterToBreak.getHitPairListPtr();
398 
399  // Calculate the doca to the PCA primary axis for each 3D hit
400  // Importantly, this also gives us the arclength along that axis to the hit
401  fPCAAlg.PCAAnalysis_calc3DDocas(clusHitPairVector, fullPCA);
402 
403  // Sort the hits along the PCA
404  clusHitPairVector.sort([](const auto& left, const auto& right){return left->getArclenToPoca() < right->getArclenToPoca();});
405 
406  // Set up container to keep track of edges
407  using DistEdgeTuple = std::tuple<float, const reco::EdgeTuple*>;
408  using DistEdgeTupleVec = std::vector<DistEdgeTuple>;
409 
410  DistEdgeTupleVec distEdgeTupleVec;
411 
412  // Now loop through all the edges and search for the furthers point
413  for(const auto& edge : clusterToBreak.getBestEdgeList())
414  {
415  const reco::ClusterHit3D* nextEdgeHit = std::get<0>(edge); // recover the first point
416 
417  // Create vector to this point from the longest edge
418  Eigen::Vector3f hitToEdgeVec(nextEdgeHit->getPosition()[0] - firstEdgeHit->getPosition()[0],
419  nextEdgeHit->getPosition()[1] - firstEdgeHit->getPosition()[1],
420  nextEdgeHit->getPosition()[2] - firstEdgeHit->getPosition()[2]);
421 
422  // Get projection
423  float hitProjection = hitToEdgeVec.dot(edgeVec);
424 
425  // Require that the point is really "opposite" the longest edge
426  if (hitProjection > 0. && hitProjection < edgeLen)
427  {
428  Eigen::Vector3f distToHitVec = hitToEdgeVec - hitProjection * edgeVec;
429  float distToHit = distToHitVec.norm();
430 
431  distEdgeTupleVec.emplace_back(distToHit,&edge);
432  }
433  }
434 
435  std::sort(distEdgeTupleVec.begin(),distEdgeTupleVec.end(),[](const auto& left,const auto& right){return std::get<0>(left) > std::get<0>(right);});
436 
437  for(const auto& distEdgeTuple : distEdgeTupleVec)
438  {
439  const reco::EdgeTuple& edgeTuple = *std::get<1>(distEdgeTuple);
440  const reco::ClusterHit3D* edgeHit = std::get<0>(edgeTuple);
441 
442  // Now find the hit identified above as furthest away
443  reco::HitPairListPtr::iterator vertexItr = std::find(clusHitPairVector.begin(),clusHitPairVector.end(),edgeHit);
444 
445  // Make sure enough hits either side, otherwise we just keep the current cluster
446  if (vertexItr == clusHitPairVector.end() || std::distance(clusHitPairVector.begin(),vertexItr) < minimumClusterSize || std::distance(vertexItr,clusHitPairVector.end()) < minimumClusterSize) continue;
447 
448  // Now we create a list of pairs of iterators to the start and end of each subcluster
449  using Hit3DItrPair = std::pair<reco::HitPairListPtr::iterator,reco::HitPairListPtr::iterator>;
450  using VertexPairList = std::list<Hit3DItrPair>;
451 
452  VertexPairList vertexPairList;
453 
454  vertexPairList.emplace_back(Hit3DItrPair(clusHitPairVector.begin(),vertexItr));
455  vertexPairList.emplace_back(Hit3DItrPair(vertexItr,clusHitPairVector.end()));
456 
457  storeCurrentCluster = false;
458 
459  // Ok, now loop through our pairs
460  for(auto& hit3DItrPair : vertexPairList)
461  {
462  reco::ClusterParameters clusterParams;
463  reco::HitPairListPtr& hitPairListPtr = clusterParams.getHitPairListPtr();
464 
465  std::cout << indent << "+> -- building new cluster, size: " << std::distance(hit3DItrPair.first,hit3DItrPair.second) << std::endl;
466 
467  // size the container...
468  hitPairListPtr.resize(std::distance(hit3DItrPair.first,hit3DItrPair.second));
469 
470  // and copy the hits into the container
471  std::copy(hit3DItrPair.first,hit3DItrPair.second,hitPairListPtr.begin());
472 
473  // First stage of feature extraction runs here
474  fPCAAlg.PCAAnalysis_3D(hitPairListPtr, clusterParams.getFullPCA());
475 
476  // Recover the new fullPCA
477  reco::PrincipalComponents& newFullPCA = clusterParams.getFullPCA();
478 
479  // Must have a valid pca
480  if (newFullPCA.getSvdOK())
481  {
482  std::cout << indent << "+> -- >> cluster has a valid Full PCA" << std::endl;
483 
484  // If the PCA's are opposite the flip the axes
485  if (fullPrimaryVec.dot(newFullPCA.getEigenVectors().row(2)) < 0.)
486  {
487  for(size_t vecIdx = 0; vecIdx < 3; vecIdx++) newFullPCA.flipAxis(vecIdx);
488  }
489 
490  // Set the skeleton PCA to make sure it has some value
491  clusterParams.getSkeletonPCA() = clusterParams.getFullPCA();
492 
493  // Be sure to compute the oonvex hull surrounding the now broken cluster
494  buildConvexHull(clusterParams, level+2);
495 
496  positionItr = breakIntoTinyBits(clusterParams, fullPCA, positionItr, outputClusterList, level+4);
497  }
498  }
499 
500  // If successful in breaking the cluster then we are done, otherwise try to loop
501  // again taking the next furthest hit
502  break;
503  }
504  }
505 
506  // First question, are we done breaking?
507  if (storeCurrentCluster)
508  {
509  // I think this is where we fill out the rest of the parameters?
510  // Start by adding the 2D hits...
511  // See if we can avoid duplicates by temporarily transferring to a set
512  std::set<const reco::ClusterHit2D*> hitSet;
513 
514  // Loop through 3D hits to get a set of unique 2D hits
515  for(const auto& hit3D : clusterToBreak.getHitPairListPtr())
516  {
517  for(const auto& hit2D : hit3D->getHits())
518  {
519  if (hit2D) hitSet.insert(hit2D);
520  }
521  }
522 
523  // Now add these to the new cluster
524  for(const auto& hit2D : hitSet)
525  {
526  hit2D->setStatusBit(reco::ClusterHit2D::USED);
527  clusterToBreak.UpdateParameters(hit2D);
528  }
529 
530  std::cout << indent << "*********>>> storing new subcluster of size " << clusterToBreak.getHitPairListPtr().size() << std::endl;
531 
532  positionItr = outputClusterList.insert(positionItr,clusterToBreak);
533 
534  // Are we filling histograms
535  if (fFillHistograms)
536  {
537  int num3DHits = clusterToBreak.getHitPairListPtr().size();
538  int numEdges = clusterToBreak.getBestEdgeList().size();
539  Eigen::Vector3f newPrimaryVec(fullPCA.getEigenVectors().row(2));
540  Eigen::Vector3f lastPrimaryVec(lastPCA.getEigenVectors().row(2));
541  float cosToLast = newPrimaryVec.dot(lastPrimaryVec);
542 
543  fSubNum3DHits->Fill(std::min(num3DHits,199), 1.);
544  fSubNumEdges->Fill(std::min(numEdges,199), 1.);
545  fSubEigen21Ratio->Fill(eigen2To1Ratio, 1.);
546  fSubEigen20Ratio->Fill(eigen2To0Ratio, 1.);
547  fSubEigen10Ratio->Fill(eigen1To0Ratio, 1.);
548  fSubCosToPrevPCA->Fill(cosToLast, 1.);
549  fSubPrimaryLength->Fill(std::min(eigenValVec[2],199.), 1.);
550  }
551 
552  // The above points to the element, want the next element
553  positionItr++;
554  }
555  else if (inputPositionItr != positionItr)
556  {
557  std::cout << indent << "***** DID NOT STORE A CLUSTER *****" << std::endl;
558  }
559 
560  return positionItr;
561 }
562 
564  reco::PrincipalComponents& lastPCA,
566  reco::ClusterParametersList& outputClusterList,
567  int level) const
568 {
569  // This is a recursive routine to divide an input cluster, according to the maximum defect point of
570  // the convex hull until we reach the point of no further improvement.
571  // The assumption is that the input cluster is fully formed already, this routine then simply
572  // divides, if successful division into two pieces it then stores the results
573 
574  // No point in doing anything if we don't have enough space points
575  if (clusterToBreak.getHitPairListPtr().size() > size_t(2 * fMinTinyClusterSize))
576  {
577  // set an indention string
578  std::string pluses(level/2, '+');
579  std::string indent(level/2, ' ');
580 
581  indent += pluses;
582 
583  // To make best use of this we'll also want the PCA for this cluster... so...
584  // Recover the prime ingredients
585  reco::PrincipalComponents& fullPCA = clusterToBreak.getFullPCA();
586  std::vector<double> eigenValVec = {3. * std::sqrt(fullPCA.getEigenValues()[0]),
587  3. * std::sqrt(fullPCA.getEigenValues()[1]),
588  3. * std::sqrt(fullPCA.getEigenValues()[2])};
589  Eigen::Vector3f fullPrimaryVec(fullPCA.getEigenVectors().row(2));
590 
591  // We want to find the convex hull vertices that lie furthest from the line to/from the extreme points
592  // To find these we:
593  // 1) recover the extreme points
594  // 2) form the vector between them
595  // 3) loop through the vertices and keep track of distance to this vector
596  // 4) Sort the resulting list by furthest points and select the one we want
597  reco::ProjectedPointList::const_iterator extremePointListItr = clusterToBreak.getConvexHull().getConvexHullExtremePoints().begin();
598 
599  const reco::ClusterHit3D* firstEdgeHit = std::get<2>(*extremePointListItr++);
600  const reco::ClusterHit3D* secondEdgeHit = std::get<2>(*extremePointListItr);
601  Eigen::Vector3f edgeVec(secondEdgeHit->getPosition()[0] - firstEdgeHit->getPosition()[0],
602  secondEdgeHit->getPosition()[1] - firstEdgeHit->getPosition()[1],
603  secondEdgeHit->getPosition()[2] - firstEdgeHit->getPosition()[2]);
604  double edgeLen = edgeVec.norm();
605 
606  // normalize it
607  edgeVec.normalize();
608 
609  // Recover the list of 3D hits associated to this cluster
610  reco::HitPairListPtr& clusHitPairVector = clusterToBreak.getHitPairListPtr();
611 
612  // Calculate the doca to the PCA primary axis for each 3D hit
613  // Importantly, this also gives us the arclength along that axis to the hit
614  fPCAAlg.PCAAnalysis_calc3DDocas(clusHitPairVector, fullPCA);
615 
616  // Sort the hits along the PCA
617  clusHitPairVector.sort([](const auto& left, const auto& right){return left->getArclenToPoca() < right->getArclenToPoca();});
618 
619  // Set up container to keep track of edges
620  using DistEdgeTuple = std::tuple<float, const reco::EdgeTuple*>;
621  using DistEdgeTupleVec = std::vector<DistEdgeTuple>;
622 
623  DistEdgeTupleVec distEdgeTupleVec;
624 
625  // Now loop through all the edges and search for the furthers point
626  for(const auto& edge : clusterToBreak.getBestEdgeList())
627  {
628  const reco::ClusterHit3D* nextEdgeHit = std::get<0>(edge); // recover the first point
629 
630  // Create vector to this point from the longest edge
631  Eigen::Vector3f hitToEdgeVec(nextEdgeHit->getPosition()[0] - firstEdgeHit->getPosition()[0],
632  nextEdgeHit->getPosition()[1] - firstEdgeHit->getPosition()[1],
633  nextEdgeHit->getPosition()[2] - firstEdgeHit->getPosition()[2]);
634 
635  // Get projection
636  float hitProjection = hitToEdgeVec.dot(edgeVec);
637 
638  // Require that the point is really "opposite" the longest edge
639  if (hitProjection > 0. && hitProjection < edgeLen)
640  {
641  Eigen::Vector3f distToHitVec = hitToEdgeVec - hitProjection * edgeVec;
642  float distToHit = distToHitVec.norm();
643 
644  distEdgeTupleVec.emplace_back(distToHit,&edge);
645  }
646  }
647 
648  std::sort(distEdgeTupleVec.begin(),distEdgeTupleVec.end(),[](const auto& left,const auto& right){return std::get<0>(left) > std::get<0>(right);});
649 
650  // Get a temporary container to hol
651  reco::ClusterParametersList tempClusterParametersList;
652  float usedDefectDist(0.);
653 
654  for(const auto& distEdgeTuple : distEdgeTupleVec)
655  {
656  const reco::EdgeTuple& edgeTuple = *std::get<1>(distEdgeTuple);
657  const reco::ClusterHit3D* edgeHit = std::get<0>(edgeTuple);
658 
659  usedDefectDist = std::get<0>(distEdgeTuple);
660 
661  // Now find the hit identified above as furthest away
662  reco::HitPairListPtr::iterator vertexItr = std::find(clusHitPairVector.begin(),clusHitPairVector.end(),edgeHit);
663 
664  // Make sure enough hits either side, otherwise we just keep the current cluster
665  if (vertexItr == clusHitPairVector.end() || std::distance(clusHitPairVector.begin(),vertexItr) < int(fMinTinyClusterSize) || std::distance(vertexItr,clusHitPairVector.end()) < int(fMinTinyClusterSize)) continue;
666 
667  tempClusterParametersList.push_back(reco::ClusterParameters());
668 
669  reco::ClusterParameters& clusterParams1 = tempClusterParametersList.back();
670 
671  if (makeCandidateCluster(fullPrimaryVec, clusterParams1, clusHitPairVector.begin(), vertexItr, level))
672  {
673  tempClusterParametersList.push_back(reco::ClusterParameters());
674 
675  reco::ClusterParameters& clusterParams2 = tempClusterParametersList.back();
676 
677  if (makeCandidateCluster(fullPrimaryVec, clusterParams2, vertexItr, clusHitPairVector.end(), level)) break;
678  }
679 
680  // If here then we could not make two valid clusters and so we try again
681  tempClusterParametersList.clear();
682  }
683 
684  // Fallback in the event of still large clusters but not defect points
685  if (tempClusterParametersList.empty())
686  {
687  std::cout << indent << "===> no cluster cands, edgeLen: " << edgeLen << ", # hits: " << clusHitPairVector.size() << ", max defect: " << std::get<0>(distEdgeTupleVec.front()) << std::endl;
688 
689  usedDefectDist = 0.;
690 
691  if (edgeLen > 20.)
692  {
693  reco::HitPairListPtr::iterator vertexItr = clusHitPairVector.begin();
694 
695  std::advance(vertexItr, clusHitPairVector.size()/2);
696 
697  tempClusterParametersList.push_back(reco::ClusterParameters());
698 
699  reco::ClusterParameters& clusterParams1 = tempClusterParametersList.back();
700 
701  if (makeCandidateCluster(fullPrimaryVec, clusterParams1, clusHitPairVector.begin(), vertexItr, level))
702  {
703  tempClusterParametersList.push_back(reco::ClusterParameters());
704 
705  reco::ClusterParameters& clusterParams2 = tempClusterParametersList.back();
706 
707  if (!makeCandidateCluster(fullPrimaryVec, clusterParams2, vertexItr, clusHitPairVector.end(), level))
708  tempClusterParametersList.clear();
709  }
710  }
711  }
712 
713  // Can only end with no candidate clusters or two so don't
714  for(auto& clusterParams : tempClusterParametersList)
715  {
716  size_t curOutputClusterListSize = outputClusterList.size();
717 
718  positionItr = subDivideCluster(clusterParams, fullPCA, positionItr, outputClusterList, level+4);
719 
720  std::cout << indent << "Output cluster list prev: " << curOutputClusterListSize << ", now: " << outputClusterList.size() << std::endl;
721 
722  // If the cluster we sent in was successfully broken then the position iterator will be shifted
723  // This means we don't want to restore the current cluster here
724  if (curOutputClusterListSize < outputClusterList.size()) continue;
725 
726  // I think this is where we fill out the rest of the parameters?
727  // Start by adding the 2D hits...
728  // See if we can avoid duplicates by temporarily transferring to a set
729  std::set<const reco::ClusterHit2D*> hitSet;
730 
731  // Loop through 3D hits to get a set of unique 2D hits
732  for(const auto& hit3D : clusterParams.getHitPairListPtr())
733  {
734  for(const auto& hit2D : hit3D->getHits())
735  {
736  if (hit2D) hitSet.insert(hit2D);
737  }
738  }
739 
740  // Now add these to the new cluster
741  for(const auto& hit2D : hitSet)
742  {
743  hit2D->setStatusBit(reco::ClusterHit2D::USED);
744  clusterParams.UpdateParameters(hit2D);
745  }
746 
747  std::cout << indent << "*********>>> storing new subcluster of size " << clusterParams.getHitPairListPtr().size() << std::endl;
748 
749  positionItr = outputClusterList.insert(positionItr,clusterParams);
750 
751  // Are we filling histograms
752  if (fFillHistograms)
753  {
754  // Recover the new fullPCA
755  reco::PrincipalComponents& newFullPCA = clusterParams.getFullPCA();
756 
757  Eigen::Vector3f newPrimaryVec(fullPCA.getEigenVectors().row(2));
758  Eigen::Vector3f lastPrimaryVec(newFullPCA.getEigenVectors().row(2));
759 
760  int num3DHits = clusterParams.getHitPairListPtr().size();
761  int numEdges = clusterParams.getBestEdgeList().size();
762  float cosToLast = newPrimaryVec.dot(lastPrimaryVec);
763  double eigen2To1Ratio = eigenValVec[0] / eigenValVec[1];
764  double eigen1To0Ratio = eigenValVec[1] / eigenValVec[2];
765  double eigen2To0Ratio = eigenValVec[2] / eigenValVec[2];
766 
767  fSubNum3DHits->Fill(std::min(num3DHits,199), 1.);
768  fSubNumEdges->Fill(std::min(numEdges,199), 1.);
769  fSubEigen21Ratio->Fill(eigen2To1Ratio, 1.);
770  fSubEigen20Ratio->Fill(eigen2To0Ratio, 1.);
771  fSubEigen10Ratio->Fill(eigen1To0Ratio, 1.);
772  fSubCosToPrevPCA->Fill(cosToLast, 1.);
773  fSubPrimaryLength->Fill(std::min(eigenValVec[2],199.), 1.);
774  fSubCosExtToPCA->Fill(fullPrimaryVec.dot(edgeVec), 1.);
775  fSubMaxDefect->Fill(std::get<0>(distEdgeTupleVec.front()), 1.);
776  fSubUsedDefect->Fill(usedDefectDist, 1.);
777  }
778 
779  // The above points to the element, want the next element
780  positionItr++;
781  }
782  }
783 
784  return positionItr;
785 }
786 
787 bool VoronoiPathFinder::makeCandidateCluster(Eigen::Vector3f& primaryPCA,
788  reco::ClusterParameters& candCluster,
789  reco::HitPairListPtr::iterator firstHitItr,
791  int level) const
792 {
793  std::string indent(level/2, ' ');
794 
795  reco::HitPairListPtr& hitPairListPtr = candCluster.getHitPairListPtr();
796 
797  std::cout << indent << "+> -- building new cluster, size: " << std::distance(firstHitItr,lastHitItr) << std::endl;
798 
799  // size the container...
800  hitPairListPtr.resize(std::distance(firstHitItr,lastHitItr));
801 
802  // and copy the hits into the container
803  std::copy(firstHitItr,lastHitItr,hitPairListPtr.begin());
804 
805  // First stage of feature extraction runs here
806  fPCAAlg.PCAAnalysis_3D(hitPairListPtr, candCluster.getFullPCA());
807 
808  // Recover the new fullPCA
809  reco::PrincipalComponents& newFullPCA = candCluster.getFullPCA();
810 
811  // Will we want to store this cluster?
812  bool keepThisCluster(false);
813 
814  // Must have a valid pca
815  if (newFullPCA.getSvdOK())
816  {
817  std::cout << indent << "+> -- >> cluster has a valid Full PCA" << std::endl;
818 
819  // Need to check if the PCA direction has been reversed
820  Eigen::Vector3f newPrimaryVec(newFullPCA.getEigenVectors().row(2));
821 
822  // If the PCA's are opposite the flip the axes
823  if (primaryPCA.dot(newPrimaryVec) < 0.)
824  {
825  for(size_t vecIdx = 0; vecIdx < 3; vecIdx++) newFullPCA.flipAxis(vecIdx);
826 
827  newPrimaryVec = Eigen::Vector3f(newFullPCA.getEigenVectors().row(2));
828  }
829 
830  // Set the skeleton PCA to make sure it has some value
831  candCluster.getSkeletonPCA() = candCluster.getFullPCA();
832 
833  // Be sure to compute the oonvex hull surrounding the now broken cluster
834  buildConvexHull(candCluster, level+2);
835 
836  std::vector<double> eigenValVec = {3. * std::sqrt(newFullPCA.getEigenValues()[0]),
837  3. * std::sqrt(newFullPCA.getEigenValues()[1]),
838  3. * std::sqrt(newFullPCA.getEigenValues()[2])};
839  double cosNewToLast = std::abs(primaryPCA.dot(newPrimaryVec));
840  double eigen2To1Ratio = eigenValVec[2] / eigenValVec[1];
841  double eigen1To0Ratio = eigenValVec[1] / eigenValVec[2];
842  double eigen2To0Ratio = eigenValVec[0] / eigenValVec[2];
843  double eigen2And1Ave = 0.5 * (eigenValVec[1] + eigenValVec[0]);
844  double eigenAveTo0Ratio = eigen2And1Ave / eigenValVec[2];
845 
846  std::cout << indent << ">>> subDivideClusters with " << candCluster.getHitPairListPtr().size() << " input hits, " << candCluster.getBestEdgeList().size() << " edges, rat21: " << eigen2To1Ratio << ", rat20: " << eigen2To0Ratio << ", rat10: " << eigen1To0Ratio << ", ave0: " << eigenAveTo0Ratio << std::endl;
847  std::cout << indent << " --> eigen 0/1/2: " << eigenValVec[0] << "/" << eigenValVec[1] << "/" << eigenValVec[2] << ", cos: " << cosNewToLast << std::endl;
848 
849  // Create a rough cut intended to tell us when we've reached the land of diminishing returns
850 // if (candCluster.getBestEdgeList().size() > 4 && cosNewToLast > 0.25 && eigen2To1Ratio < 0.9 && eigen2To0Ratio > 0.001)
851  if (candCluster.getBestEdgeList().size() > 4 && cosNewToLast > 0.25 && eigen2To1Ratio > 0.01 && eigen2To1Ratio < 0.99 && eigen1To0Ratio < 0.5)
852  {
853  keepThisCluster = true;
854  }
855  }
856 
857  return keepThisCluster;
858 }
859 
860 
862 {
863  // set an indention string
864  std::string minuses(level/2, '-');
865  std::string indent(level/2, ' ');
866 
867  indent += minuses;
868 
869  // The plan is to build the enclosing 2D polygon around the points in the PCA plane of most spread for this cluster
870  // To do so we need to start by building a list of 2D projections onto the plane of most spread...
871  reco::PrincipalComponents& pca = clusterParameters.getFullPCA();
872 
873  // Recover the parameters from the Principal Components Analysis that we need to project and accumulate
874  Eigen::Vector3f pcaCenter(pca.getAvePosition()[0],pca.getAvePosition()[1],pca.getAvePosition()[2]);
875 
876  //dcel2d::PointList pointList;
877  using Point = std::tuple<float,float,const reco::ClusterHit3D*>;
878  using PointList = std::list<Point>;
879 
880  reco::ConvexHull& convexHull = clusterParameters.getConvexHull();
881  PointList pointList = convexHull.getProjectedPointList();
882 
883  // Loop through hits and do projection to plane
884  for(const auto& hit3D : clusterParameters.getHitPairListPtr())
885  {
886  Eigen::Vector3f pcaToHitVec(hit3D->getPosition()[0] - pcaCenter(0),
887  hit3D->getPosition()[1] - pcaCenter(1),
888  hit3D->getPosition()[2] - pcaCenter(2));
889  Eigen::Vector3f pcaToHit = pca.getEigenVectors() * pcaToHitVec;
890 
891  pointList.emplace_back(dcel2d::Point(pcaToHit(0),pcaToHit(1),hit3D));
892  }
893 
894  // Sort the point vec by increasing x, then increase y
895  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);});
896 
897  // containers for finding the "best" hull...
898  std::vector<ConvexHull> convexHullVec;
899  std::vector<PointList> rejectedListVec;
900  bool increaseDepth(pointList.size() > 5);
901  float lastArea(std::numeric_limits<float>::max());
902 
903  while(increaseDepth)
904  {
905  // Get another convexHull container
906  convexHullVec.push_back(ConvexHull(pointList));
907  rejectedListVec.push_back(PointList());
908 
909  const ConvexHull& convexHull = convexHullVec.back();
910  PointList& rejectedList = rejectedListVec.back();
911  const PointList& convexHullPoints = convexHull.getConvexHull();
912 
913  increaseDepth = false;
914 
915  if (convexHull.getConvexHullArea() > 0.)
916  {
917  std::cout << indent << "-> built convex hull, 3D hits: " << pointList.size() << " with " << convexHullPoints.size() << " vertices" << ", area: " << convexHull.getConvexHullArea() << std::endl;
918  std::cout << indent << "-> -Points:";
919  for(const auto& point : convexHullPoints)
920  std::cout << " (" << std::get<0>(point) << "," << std::get<1>(point) << ")";
921  std::cout << std::endl;
922 
923  if (convexHullVec.size() < 2 || convexHull.getConvexHullArea() < 0.8 * lastArea)
924  {
925  for(auto& point : convexHullPoints)
926  {
927  pointList.remove(point);
928  rejectedList.emplace_back(point);
929  }
930  lastArea = convexHull.getConvexHullArea();
931 // increaseDepth = true;
932  }
933  }
934  }
935 
936  // do we have a valid convexHull?
937  while(!convexHullVec.empty() && convexHullVec.back().getConvexHullArea() < 0.5)
938  {
939  convexHullVec.pop_back();
940  rejectedListVec.pop_back();
941  }
942 
943  // If we found the convex hull then build edges around the region
944  if (!convexHullVec.empty())
945  {
946  size_t nRejectedTotal(0);
947  reco::HitPairListPtr hitPairListPtr = clusterParameters.getHitPairListPtr();
948 
949  for(const auto& rejectedList : rejectedListVec)
950  {
951  nRejectedTotal += rejectedList.size();
952 
953  for(const auto& rejectedPoint : rejectedList)
954  {
955  std::cout << indent << "-> -- Point is " << convexHullVec.back().findNearestDistance(rejectedPoint) << " from nearest edge" << std::endl;
956 
957  if (convexHullVec.back().findNearestDistance(rejectedPoint) > 0.5)
958  hitPairListPtr.remove(std::get<2>(rejectedPoint));
959  }
960  }
961 
962  std::cout << indent << "-> Removed " << nRejectedTotal << " leaving " << pointList.size() << "/" << hitPairListPtr.size() << " points" << std::endl;
963 
964  // Now add "edges" to the cluster to describe the convex hull (for the display)
965  reco::Hit3DToEdgeMap& edgeMap = convexHull.getConvexHullEdgeMap();
966  reco::EdgeList& bestEdgeList = convexHull.getConvexHullEdgeList();
967 
968  Point lastPoint = convexHullVec.back().getConvexHull().front();
969 
970  for(auto& curPoint : convexHullVec.back().getConvexHull())
971  {
972  if (curPoint == lastPoint) continue;
973 
974  const reco::ClusterHit3D* lastPoint3D = std::get<2>(lastPoint);
975  const reco::ClusterHit3D* curPoint3D = std::get<2>(curPoint);
976 
977  float distBetweenPoints = (curPoint3D->getPosition()[0] - lastPoint3D->getPosition()[0]) * (curPoint3D->getPosition()[0] - lastPoint3D->getPosition()[0])
978  + (curPoint3D->getPosition()[1] - lastPoint3D->getPosition()[1]) * (curPoint3D->getPosition()[1] - lastPoint3D->getPosition()[1])
979  + (curPoint3D->getPosition()[2] - lastPoint3D->getPosition()[2]) * (curPoint3D->getPosition()[2] - lastPoint3D->getPosition()[2]);
980 
981  distBetweenPoints = std::sqrt(distBetweenPoints);
982 
983  reco::EdgeTuple edge(lastPoint3D,curPoint3D,distBetweenPoints);
984 
985  edgeMap[lastPoint3D].push_back(edge);
986  edgeMap[curPoint3D].push_back(edge);
987  bestEdgeList.emplace_back(edge);
988 
989  lastPoint = curPoint;
990  }
991 
992  // Store the "extreme" points
993  const ConvexHull::PointList& extremePoints = convexHullVec.back().getExtremePoints();
994  reco::ProjectedPointList& extremePointList = convexHull.getConvexHullExtremePoints();
995 
996  for(const auto& point : extremePoints) extremePointList.push_back(point);
997  }
998 
999  return;
1000 }
1001 
1002 
1004 {
1005  // The plan is to build the enclosing 2D polygon around the points in the PCA plane of most spread for this cluster
1006  // To do so we need to start by building a list of 2D projections onto the plane of most spread...
1007  reco::PrincipalComponents& pca = clusterParameters.getFullPCA();
1008 
1009  // Recover the parameters from the Principal Components Analysis that we need to project and accumulate
1010  Eigen::Vector3f pcaCenter(pca.getAvePosition()[0],pca.getAvePosition()[1],pca.getAvePosition()[2]);
1011 
1012  dcel2d::PointList pointList;
1013 
1014  // Loop through hits and do projection to plane
1015  for(const auto& hit3D : clusterParameters.getHitPairListPtr())
1016  {
1017  Eigen::Vector3f pcaToHitVec(hit3D->getPosition()[0] - pcaCenter(0),
1018  hit3D->getPosition()[1] - pcaCenter(1),
1019  hit3D->getPosition()[2] - pcaCenter(2));
1020  Eigen::Vector3f pcaToHit = pca.getEigenVectors() * pcaToHitVec;
1021 
1022  pointList.emplace_back(dcel2d::Point(pcaToHit(1),pcaToHit(2),hit3D));
1023  }
1024 
1025  // Sort the point vec by increasing x, then increase y
1026  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);});
1027 
1028  std::cout << " ==> Build V diagram, sorted point list contains " << pointList.size() << " hits" << std::endl;
1029 
1030  // Set up the voronoi diagram builder
1031  voronoi2d::VoronoiDiagram voronoiDiagram(clusterParameters.getHalfEdgeList(),clusterParameters.getVertexList(),clusterParameters.getFaceList());
1032 
1033  // And make the diagram
1034  voronoiDiagram.buildVoronoiDiagram(pointList);
1035 
1036  // Recover the voronoi diagram vertex list and the container to store them in
1037  dcel2d::VertexList& vertexList = clusterParameters.getVertexList();
1038 
1039  // Now get the inverse of the rotation matrix so we can get the vertex positions,
1040  // which lie in the plane of the two largest PCA axes, in the standard coordinate system
1041  Eigen::Matrix3f rotationMatrixInv = pca.getEigenVectors().inverse();
1042 
1043  // Translate and fill
1044  for(auto& vertex : vertexList)
1045  {
1046  Eigen::Vector3f coords = rotationMatrixInv * vertex.getCoords();
1047 
1048  coords += pcaCenter;
1049 
1050  vertex.setCoords(coords);
1051  }
1052 
1053  // Now do the Convex Hull
1054  // Now add "edges" to the cluster to describe the convex hull (for the display)
1055  reco::Hit3DToEdgeMap& edgeMap = clusterParameters.getHit3DToEdgeMap();
1056  reco::EdgeList& bestEdgeList = clusterParameters.getBestEdgeList();
1057 
1058 // const dcel2d::PointList& edgePoints = voronoiDiagram.getConvexHull();
1059 // PointList localList;
1060 //
1061 // for(const auto& edgePoint : edgePoints) localList.emplace_back(std::get<0>(edgePoint),std::get<1>(edgePoint),std::get<2>(edgePoint));
1062 //
1063 // // Sort the point vec by increasing x, then increase y
1064 // 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);});
1065 //
1066 // // Why do I need to do this?
1067 // ConvexHull convexHull(localList);
1068 //
1069 // Point lastPoint = convexHull.getConvexHull().front();
1070  dcel2d::Point lastPoint = voronoiDiagram.getConvexHull().front();
1071 
1072 // std::cout << "@@@@>> Build convex hull, voronoi handed " << edgePoints.size() << " points, convexHull cut to " << convexHull.getConvexHull().size() << std::endl;
1073 
1074 // for(auto& curPoint : convexHull.getConvexHull())
1075  for(auto& curPoint : voronoiDiagram.getConvexHull())
1076  {
1077  if (curPoint == lastPoint) continue;
1078 
1079  const reco::ClusterHit3D* lastPoint3D = std::get<2>(lastPoint);
1080  const reco::ClusterHit3D* curPoint3D = std::get<2>(curPoint);
1081 
1082  float distBetweenPoints = (curPoint3D->getPosition()[0] - lastPoint3D->getPosition()[0]) * (curPoint3D->getPosition()[0] - lastPoint3D->getPosition()[0])
1083  + (curPoint3D->getPosition()[1] - lastPoint3D->getPosition()[1]) * (curPoint3D->getPosition()[1] - lastPoint3D->getPosition()[1])
1084  + (curPoint3D->getPosition()[2] - lastPoint3D->getPosition()[2]) * (curPoint3D->getPosition()[2] - lastPoint3D->getPosition()[2]);
1085 
1086  distBetweenPoints = std::sqrt(distBetweenPoints);
1087 
1088  reco::EdgeTuple edge(lastPoint3D,curPoint3D,distBetweenPoints);
1089 
1090  edgeMap[lastPoint3D].push_back(edge);
1091  edgeMap[curPoint3D].push_back(edge);
1092  bestEdgeList.emplace_back(edge);
1093 
1094  lastPoint = curPoint;
1095  }
1096 
1097  std::cout << "****> vertexList containted " << vertexList.size() << " vertices for " << clusterParameters.getHitPairListPtr().size() << " hits" << std::endl;
1098 
1099  return;
1100 }
1101 
1102 
1103 float VoronoiPathFinder::closestApproach(const Eigen::Vector3f& P0,
1104  const Eigen::Vector3f& u0,
1105  const Eigen::Vector3f& P1,
1106  const Eigen::Vector3f& u1,
1107  Eigen::Vector3f& poca0,
1108  Eigen::Vector3f& poca1) const
1109 {
1110  // Technique is to compute the arclength to each point of closest approach
1111  Eigen::Vector3f w0 = P0 - P1;
1112  float a(1.);
1113  float b(u0.dot(u1));
1114  float c(1.);
1115  float d(u0.dot(w0));
1116  float e(u1.dot(w0));
1117  float den(a * c - b * b);
1118 
1119  float arcLen0 = (b * e - c * d) / den;
1120  float arcLen1 = (a * e - b * d) / den;
1121 
1122  poca0 = P0 + arcLen0 * u0;
1123  poca1 = P1 + arcLen1 * u1;
1124 
1125  return (poca0 - poca1).norm();
1126 }
1127 
1128 float VoronoiPathFinder::findConvexHullEndPoints(const reco::EdgeList& convexHull, const reco::ClusterHit3D* first3D, const reco::ClusterHit3D* last3D) const
1129 {
1130  float largestDistance(0.);
1131 
1132  // Note that edges are vectors and that the convex hull edge list will be ordered
1133  // The idea is that the maximum distance from a given edge is to the edge just before the edge that "turns back" towards the current edge
1134  // meaning that the dot product of the two edges becomes negative.
1135  reco::EdgeList::const_iterator firstEdgeItr = convexHull.begin();
1136 
1137  while(firstEdgeItr != convexHull.end())
1138  {
1139  reco::EdgeList::const_iterator nextEdgeItr = firstEdgeItr;
1140 
1141 // Eigen::Vector2f firstEdgeVec(std::get<3>(*firstEdgeItr),std::get<);
1142 // Eigen::Vector2f lastPrimaryVec(lastPCA.getEigenVectors()[0][0],lastPCA.getEigenVectors()[0][1],lastPCA.getEigenVectors()[0][2]);
1143 // float cosToLast = newPrimaryVec.dot(lastPrimaryVec);
1144 
1145  while(++nextEdgeItr != convexHull.end())
1146  {
1147 
1148  }
1149  }
1150 
1151  return largestDistance;
1152 }
1153 
1155 } // 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
float getTimeToExecute() const override
If monitoring, recover the time to execute a particular function.
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
std::list< ProjectedPoint > ProjectedPointList
Definition: Cluster3D.h:353
float closestApproach(const Eigen::Vector3f &, const Eigen::Vector3f &, const Eigen::Vector3f &, const Eigen::Vector3f &, Eigen::Vector3f &, Eigen::Vector3f &) const
reco::PrincipalComponents & getSkeletonPCA()
Definition: Cluster3D.h:478
std::list< Point > PointList
The list of the projected points.
Definition: ConvexHull.h:34
reco::EdgeList & getBestEdgeList()
Definition: Cluster3D.h:481
std::pair< Point, Point > MinMaxPoints
reco::Hit3DToEdgeMap & getHit3DToEdgeMap()
Definition: Cluster3D.h:479
intermediate_table::const_iterator const_iterator
reco::HitPairListPtr & getHitPairListPtr()
Definition: Cluster3D.h:476
string dir
Cluster finding and building.
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
const EigenValues & getEigenValues() const
Definition: Cluster3D.h:246
const double e
VoronoiDiagram class definiton.
Definition: Voronoi.h:31
reco::PrincipalComponents & getFullPCA()
Definition: Cluster3D.h:477
reco::Hit3DToEdgeMap & getConvexHullEdgeMap()
Definition: Cluster3D.h:385
std::unique_ptr< lar_cluster3d::IClusterAlg > fClusterAlg
Tools.
std::tuple< double, double, const reco::ClusterHit3D * > Point
Definitions used by the VoronoiDiagram algorithm.
Definition: DCEL.h:44
reco::ProjectedPointList & getConvexHullExtremePoints()
Definition: Cluster3D.h:387
const double a
void buildVoronoiDiagram(const dcel2d::PointList &)
Given an input set of 2D points construct a 2D voronoi diagram.
Definition: Voronoi.cxx:133
void ModifyClusters(reco::ClusterParametersList &) const override
Scan an input collection of clusters and modify those according to the specific implementing algorith...
Define a container for working with the convex hull.
Definition: Cluster3D.h:360
void initializeHistograms(art::TFileDirectory &) override
Interface for initializing histograms if they are desired Note that the idea is to put hisgtograms in...
T get(std::string const &key) const
Definition: ParameterSet.h:271
const Eigen::Vector3f & getAvePosition() const
Definition: Cluster3D.h:248
void buildConvexHull(reco::ClusterParameters &clusterParameters, int level=0) const
reco::ClusterParametersList::iterator subDivideCluster(reco::ClusterParameters &cluster, reco::PrincipalComponents &lastPCA, reco::ClusterParametersList::iterator positionItr, reco::ClusterParametersList &outputClusterList, int level=0) const
Use PCA to try to find path in 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
ConvexHull class definiton.
Definition: ConvexHull.h:27
reco::ConvexHull & getConvexHull()
Definition: Cluster3D.h:482
T min(sqlite3 *const db, std::string const &table_name, std::string const &column_name)
Definition: statistics.h:55
reco::ClusterParametersList::iterator breakIntoTinyBits(reco::ClusterParameters &cluster, reco::PrincipalComponents &lastPCA, reco::ClusterParametersList::iterator positionItr, reco::ClusterParametersList &outputClusterList, int level=0) const
Use PCA to try to find path in cluster.
dcel2d::FaceList & getFaceList()
Definition: Cluster3D.h:483
MaybeLogger_< ELseverityLevel::ELsev_success, false > LogDebug
This provides an art tool interface definition for 3D Cluster algorithms.
T copy(T const &v)
void buildVoronoiDiagram(reco::ClusterParameters &clusterParameters, int level=0) const
static bool * b
Definition: config.cpp:1043
bool makeCandidateCluster(Eigen::Vector3f &, reco::ClusterParameters &, reco::HitPairListPtr::iterator, reco::HitPairListPtr::iterator, int) const
size_t fMinTinyClusterSize
Minimum size for a "tiny" cluster.
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
std::pair< MinMaxPoints, MinMaxPoints > MinMaxPointPair
reco::ProjectedPointList & getProjectedPointList()
Definition: Cluster3D.h:383
reco::EdgeList & getConvexHullEdgeList()
Definition: Cluster3D.h:386
void start()
Definition: cpu_timer.cc:83
dcel2d::HalfEdgeList & getHalfEdgeList()
Definition: Cluster3D.h:485
std::list< Vertex > VertexList
Definition: DCEL.h:182
bool fFillHistograms
Histogram definitions.
std::list< ClusterParameters > ClusterParametersList
Definition: Cluster3D.h:404
VoronoiPathFinder(const fhicl::ParameterSet &)
Constructor.
const EigenVectors & getEigenVectors() const
Definition: Cluster3D.h:247
QTextStream & endl(QTextStream &s)
dcel2d::VertexList & getVertexList()
Definition: Cluster3D.h:484
void configure(fhicl::ParameterSet const &pset) override
float findConvexHullEndPoints(const reco::EdgeList &, const reco::ClusterHit3D *, const reco::ClusterHit3D *) const
vertex reconstruction