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