LArSoft  v07_13_02
Liquid Argon Software toolkit - http://larsoft.org/
ConvexHullPathFinder_tool.cc
Go to the documentation of this file.
1 
8 // Framework Includes
12 #include "cetlib/search_path.h"
13 #include "cetlib/cpu_timer.h"
14 
17 
18 // LArSoft includes
26 
27 // Eigen
28 #include <Eigen/Dense>
29 
30 // Root histograms
31 #include "TH1F.h"
32 #include "TH2F.h"
33 #include "TProfile.h"
34 
35 // std includes
36 #include <string>
37 #include <functional>
38 #include <iostream>
39 #include <memory>
40 
41 //------------------------------------------------------------------------------------------------------------------------------------------
42 // implementation follows
43 
44 namespace lar_cluster3d {
45 
46 class ConvexHullPathFinder : virtual public IClusterModAlg
47 {
48 public:
55 
60 
61  void configure(fhicl::ParameterSet const &pset) override;
62 
70 
77  void ModifyClusters(reco::ClusterParametersList&) const override;
78 
82  float getTimeToExecute() const override {return fTimeToProcess;}
83 
84 private:
85 
95  reco::ClusterParametersList& outputClusterList,
96  int level = 0) const;
97 
98  using HitOrderTuple = std::tuple<float,float,reco::ProjectedPoint>;
99  using HitOrderTupleList = std::list<HitOrderTuple>;
100 
101  bool makeCandidateCluster(Eigen::Vector3f&,
105  int) const;
106 
107  bool makeCandidateCluster(Eigen::Vector3f&,
110  int) const;
111 
112  bool completeCandidateCluster(Eigen::Vector3f&, reco::ClusterParameters&, int) const;
113 
119 
120  float closestApproach(const Eigen::Vector3f&,
121  const Eigen::Vector3f&,
122  const Eigen::Vector3f&,
123  const Eigen::Vector3f&,
124  Eigen::Vector3f&,
125  Eigen::Vector3f&) const;
126 
127  using MinMaxPoints = std::pair<reco::ProjectedPoint,reco::ProjectedPoint>;
128  using MinMaxPointPair = std::pair<MinMaxPoints,MinMaxPoints>;
129 
130  void buildConvexHull(reco::ClusterParameters& clusterParameters, int level = 0) const;
131 
133 
134  using KinkTuple = std::tuple<int, reco::ConvexHullKinkTuple, HitOrderTupleList, HitOrderTupleList>;
135  using KinkTupleVec = std::vector<KinkTuple>;
136 
137  void orderHitsAlongEdge(const reco::ProjectedPointList&, const reco::ProjectedPoint&, const Eigen::Vector2f&, HitOrderTupleList&) const;
138 
140 
148  mutable float fTimeToProcess;
149 
154 
161 
172 
176  geo::Geometry* fGeometry; //< pointer to the Geometry service
177 
178  std::unique_ptr<lar_cluster3d::IClusterAlg> fClusterAlg;
179  PrincipalComponentsAlg fPCAAlg; // For running Principal Components Analysis
180 };
181 
183  fPCAAlg(pset.get<fhicl::ParameterSet>("PrincipalComponentsAlg"))
184 {
185  this->configure(pset);
186 }
187 
188 //------------------------------------------------------------------------------------------------------------------------------------------
189 
191 {
192 }
193 
194 //------------------------------------------------------------------------------------------------------------------------------------------
195 
197 {
198  fEnableMonitoring = pset.get<bool> ("EnableMonitoring", true );
199  fMinTinyClusterSize = pset.get<size_t>("MinTinyClusterSize", 40 );
200  fConvexHullKinkAngle = pset.get<float >("ConvexHullKinkAgle", 0.92);
201  fConvexHullMinSep = pset.get<float >("ConvexHullMinSep", 0.65);
202  fClusterAlg = art::make_tool<lar_cluster3d::IClusterAlg>(pset.get<fhicl::ParameterSet>("ClusterAlg"));
203 
205 
206  fGeometry = &*geometry;
207 
208  fTimeToProcess = 0.;
209 
210  return;
211 }
212 
214 {
215  // It is assumed that the input TFileDirectory has been set up to group histograms into a common
216  // folder at the calling routine's level. Here we create one more level of indirection to keep
217  // histograms made by this tool separate.
218  fFillHistograms = true;
219 
220  std::string dirName = "ConvexHullPath";
221 
222  art::TFileDirectory dir = histDir.mkdir(dirName.c_str());
223 
224  // Divide into two sets of hists... those for the overall cluster and
225  // those for the subclusters
226  fTopNum3DHits = dir.make<TH1F>("TopNum3DHits", "Number 3D Hits", 200, 0., 200.);
227  fTopNumEdges = dir.make<TH1F>("TopNumEdges", "Number Edges", 200, 0., 200.);
228  fTopEigen21Ratio = dir.make<TH1F>("TopEigen21Rat", "Eigen 2/1 Ratio", 100, 0., 1.);
229  fTopEigen20Ratio = dir.make<TH1F>("TopEigen20Rat", "Eigen 2/0 Ratio", 100, 0., 1.);
230  fTopEigen10Ratio = dir.make<TH1F>("TopEigen10Rat", "Eigen 1/0 Ratio", 100, 0., 1.);
231  fTopPrimaryLength = dir.make<TH1F>("TopPrimaryLen", "Primary Length", 200, 0., 200.);
232 
233  fSubNum3DHits = dir.make<TH1F>("SubNum3DHits", "Number 3D Hits", 200, 0., 200.);
234  fSubNumEdges = dir.make<TH1F>("SubNumEdges", "Number Edges", 200, 0., 200.);
235  fSubEigen21Ratio = dir.make<TH1F>("SubEigen21Rat", "Eigen 2/1 Ratio", 100, 0., 1.);
236  fSubEigen20Ratio = dir.make<TH1F>("SubEigen20Rat", "Eigen 2/0 Ratio", 100, 0., 1.);
237  fSubEigen10Ratio = dir.make<TH1F>("SubEigen10Rat", "Eigen 1/0 Ratio", 100, 0., 1.);
238  fSubPrimaryLength = dir.make<TH1F>("SubPrimaryLen", "Primary Length", 200, 0., 200.);
239  fSubCosToPrevPCA = dir.make<TH1F>("SubCosToPrev", "Cos(theta)", 101, 0., 1.01);
240  fSubCosExtToPCA = dir.make<TH1F>("SubCosExtPCA", "Cos(theta)", 102, -1.01, 1.01);
241  fSubMaxDefect = dir.make<TH1F>("SubMaxDefect", "Max Defect", 100, 0., 50.);
242  fSubUsedDefect = dir.make<TH1F>("SubUsedDefect", "Used Defect", 100, 0., 50.);
243 
244  return;
245 }
246 
248 {
256  // Initial clustering is done, now trim the list and get output parameters
257  cet::cpu_timer theClockBuildClusters;
258 
259  // Start clocks if requested
260  if (fEnableMonitoring) theClockBuildClusters.start();
261 
262  // This is the loop over candidate 3D clusters
263  // Note that it might be that the list of candidate clusters is modified by splitting
264  // So we use the following construct to make sure we get all of them
265  reco::ClusterParametersList::iterator clusterParametersListItr = clusterParametersList.begin();
266 
267  while(clusterParametersListItr != clusterParametersList.end())
268  {
269  // Dereference to get the cluster paramters
270  reco::ClusterParameters& clusterParameters = *clusterParametersListItr;
271 
272  // It turns out that computing the convex hull surrounding the points in the 2D projection onto the
273  // plane of largest spread in the PCA is a good way to break up the cluster... and we do it here since
274  // we (currently) want this to be part of the standard output
275  buildConvexHull(clusterParameters);
276 
277  // Make sure our cluster has enough hits...
278  if (clusterParameters.getHitPairListPtr().size() > fMinTinyClusterSize)
279  {
280  // Get an interim cluster list
281  reco::ClusterParametersList reclusteredParameters;
282 
283  // Call the main workhorse algorithm for building the local version of candidate 3D clusters
284  //******** Remind me why we need to call this at this point when the same hits will be used? ********
285  //fClusterAlg->Cluster3DHits(clusterParameters.getHitPairListPtr(), reclusteredParameters);
286  reclusteredParameters.push_back(clusterParameters);
287 
288  // Only process non-empty results
289  if (!reclusteredParameters.empty())
290  {
291  // Loop over the reclustered set
292  for (auto& cluster : reclusteredParameters)
293  {
294  // It turns out that computing the convex hull surrounding the points in the 2D projection onto the
295  // plane of largest spread in the PCA is a good way to break up the cluster... and we do it here since
296  // we (currently) want this to be part of the standard output
298 
299  // Break our cluster into smaller elements...
300  subDivideCluster(cluster, cluster.getFullPCA(), cluster.daughterList().end(), cluster.daughterList(), 4);
301 
302  // Add the daughters to the cluster
303  clusterParameters.daughterList().insert(clusterParameters.daughterList().end(),cluster);
304 
305  // If filling histograms we do the main cluster here
306  if (fFillHistograms)
307  {
308  reco::PrincipalComponents& fullPCA = cluster.getFullPCA();
309  std::vector<double> eigenValVec = {3. * std::sqrt(fullPCA.getEigenValues()[0]),
310  3. * std::sqrt(fullPCA.getEigenValues()[1]),
311  3. * std::sqrt(fullPCA.getEigenValues()[2])};
312  double eigen2To1Ratio = eigenValVec[2] / eigenValVec[1];
313  double eigen1To0Ratio = eigenValVec[1] / eigenValVec[0];
314  double eigen2To0Ratio = eigenValVec[2] / eigenValVec[0];
315  int num3DHits = cluster.getHitPairListPtr().size();
316  int numEdges = cluster.getBestEdgeList().size();
317 
318  fTopNum3DHits->Fill(std::min(num3DHits,199), 1.);
319  fTopNumEdges->Fill(std::min(numEdges,199), 1.);
320  fTopEigen21Ratio->Fill(eigen2To1Ratio, 1.);
321  fTopEigen20Ratio->Fill(eigen2To0Ratio, 1.);
322  fTopEigen10Ratio->Fill(eigen1To0Ratio, 1.);
323  fTopPrimaryLength->Fill(std::min(eigenValVec[0],199.), 1.);
324  }
325  }
326  }
327  }
328 
329  // Go to next cluster parameters object
330  clusterParametersListItr++;
331  }
332 
333  if (fEnableMonitoring)
334  {
335  theClockBuildClusters.stop();
336 
337  fTimeToProcess = theClockBuildClusters.accumulated_real_time();
338  }
339 
340  mf::LogDebug("Cluster3D") << ">>>>> Cluster Path finding done" << std::endl;
341 
342  return;
343 }
344 
346  reco::PrincipalComponents& lastPCA,
348  reco::ClusterParametersList& outputClusterList,
349  int level) const
350 {
351  // This is a recursive routine to divide an input cluster, according to the maximum defect point of
352  // the convex hull until we reach the point of no further improvement.
353  // The assumption is that the input cluster is fully formed already, this routine then simply
354  // divides, if successful division into two pieces it then stores the results
355 
356  // No point in doing anything if we don't have enough space points
357  if (clusterToBreak.getHitPairListPtr().size() > size_t(2 * fMinTinyClusterSize))
358  {
359  // set an indention string
360 // std::string pluses(level/2, '+');
361 // std::string indent(level/2, ' ');
362 //
363 // indent += pluses;
364 
365  // We want to find the convex hull vertices that lie furthest from the line to/from the extreme points
366  // To find these we:
367  // 1) recover the extreme points
368  // 2) form the vector between them
369  // 3) loop through the vertices and keep track of distance to this vector
370  // 4) Sort the resulting list by furthest points and select the one we want
371  reco::ConvexHull& convexHull = clusterToBreak.getConvexHull();
372 
373  reco::ProjectedPointList::const_iterator extremePointListItr = convexHull.getConvexHullExtremePoints().begin();
374 
375  const reco::ClusterHit3D* firstEdgeHit = std::get<2>(*extremePointListItr++);
376  const reco::ClusterHit3D* secondEdgeHit = std::get<2>(*extremePointListItr);
377  Eigen::Vector3f edgeVec(secondEdgeHit->getPosition()[0] - firstEdgeHit->getPosition()[0],
378  secondEdgeHit->getPosition()[1] - firstEdgeHit->getPosition()[1],
379  secondEdgeHit->getPosition()[2] - firstEdgeHit->getPosition()[2]);
380  double edgeLen = edgeVec.norm();
381 
382  // normalize it
383  edgeVec.normalize();
384 
385  // Recover the list of 3D hits associated to this cluster
386  reco::HitPairListPtr& clusHitPairVector = clusterToBreak.getHitPairListPtr();
387 
388  // Recover the PCA for the input cluster
389  reco::PrincipalComponents& fullPCA = clusterToBreak.getFullPCA();
390  Eigen::Vector3f fullPrimaryVec(fullPCA.getEigenVectors()[0][0],fullPCA.getEigenVectors()[0][1],fullPCA.getEigenVectors()[0][2]);
391 
392  // Calculate the doca to the PCA primary axis for each 3D hit
393  // Importantly, this also gives us the arclength along that axis to the hit
394  fPCAAlg.PCAAnalysis_calc3DDocas(clusHitPairVector, fullPCA);
395 
396  // Sort the hits along the PCA
397  clusHitPairVector.sort([](const auto& left, const auto& right){return left->getArclenToPoca() < right->getArclenToPoca();});
398 
399  // Get a temporary container to hol
400  reco::ClusterParametersList tempClusterParametersList;
401 
402  // Try breaking clusters by finding the "maximum defect" point.
403  // If this fails the fallback in the event of still large clusters is to split in half
404 // if (!breakClusterByMaxDefect(clusterToBreak, clusHitPairVector, tempClusterParametersList, level))
405  if (!breakClusterByKinks(clusterToBreak, clusHitPairVector, tempClusterParametersList, level))
406  {
407  // Look to see if we can break at a gap
408  if (!breakClusterAtBigGap(clusterToBreak, clusHitPairVector, tempClusterParametersList, level))
409  {
410  // It might be that we have a large deviation in the convex hull...
411  if (!breakClusterByMaxDefect(clusterToBreak, clusHitPairVector, tempClusterParametersList, level))
412  // Well, we don't want "flippers" so make sure the edge has some teeth to it
413  if (edgeLen > 10.) breakClusterInHalf(clusterToBreak, clusHitPairVector, tempClusterParametersList, level);
414  }
415  }
416 
417  // Can only end with no candidate clusters or two so don't
418  for(auto& clusterParams : tempClusterParametersList)
419  {
420  size_t curOutputClusterListSize = outputClusterList.size();
421 
422  positionItr = subDivideCluster(clusterParams, fullPCA, positionItr, outputClusterList, level+4);
423 
424  // If the cluster we sent in was successfully broken then the position iterator will be shifted
425  // This means we don't want to restore the current cluster here
426  if (curOutputClusterListSize < outputClusterList.size()) continue;
427 
428  // I think this is where we fill out the rest of the parameters?
429  // Start by adding the 2D hits...
430  // See if we can avoid duplicates by temporarily transferring to a set
431  std::set<const reco::ClusterHit2D*> hitSet;
432 
433  // Loop through 3D hits to get a set of unique 2D hits
434  for(const auto& hit3D : clusterParams.getHitPairListPtr())
435  {
436  for(const auto& hit2D : hit3D->getHits())
437  {
438  if (hit2D) hitSet.insert(hit2D);
439  }
440  }
441 
442  // Now add these to the new cluster
443  for(const auto& hit2D : hitSet)
444  {
445  hit2D->setStatusBit(reco::ClusterHit2D::USED);
446  clusterParams.UpdateParameters(hit2D);
447  }
448 
449  positionItr = outputClusterList.insert(positionItr,clusterParams);
450 
451  // Are we filling histograms
452  if (fFillHistograms)
453  {
454  std::vector<double> eigenValVec = {3. * std::sqrt(fullPCA.getEigenValues()[0]),
455  3. * std::sqrt(fullPCA.getEigenValues()[1]),
456  3. * std::sqrt(fullPCA.getEigenValues()[2])};
457 
458  // Recover the new fullPCA
459  reco::PrincipalComponents& newFullPCA = clusterParams.getFullPCA();
460 
461  Eigen::Vector3f newPrimaryVec(fullPCA.getEigenVectors()[0][0],fullPCA.getEigenVectors()[0][1],fullPCA.getEigenVectors()[0][2]);
462  Eigen::Vector3f lastPrimaryVec(newFullPCA.getEigenVectors()[0][0],newFullPCA.getEigenVectors()[0][1],newFullPCA.getEigenVectors()[0][2]);
463 
464  int num3DHits = clusterParams.getHitPairListPtr().size();
465  int numEdges = clusterParams.getBestEdgeList().size();
466  float cosToLast = newPrimaryVec.dot(lastPrimaryVec);
467  double eigen2To1Ratio = eigenValVec[2] / eigenValVec[1];
468  double eigen1To0Ratio = eigenValVec[1] / eigenValVec[0];
469  double eigen2To0Ratio = eigenValVec[2] / eigenValVec[0];
470 
471  fSubNum3DHits->Fill(std::min(num3DHits,199), 1.);
472  fSubNumEdges->Fill(std::min(numEdges,199), 1.);
473  fSubEigen21Ratio->Fill(eigen2To1Ratio, 1.);
474  fSubEigen20Ratio->Fill(eigen2To0Ratio, 1.);
475  fSubEigen10Ratio->Fill(eigen1To0Ratio, 1.);
476  fSubCosToPrevPCA->Fill(cosToLast, 1.);
477  fSubPrimaryLength->Fill(std::min(eigenValVec[0],199.), 1.);
478  fSubCosExtToPCA->Fill(fullPrimaryVec.dot(edgeVec), 1.);
479  }
480 
481  // The above points to the element, want the next element
482  positionItr++;
483  }
484  }
485 
486  return positionItr;
487 }
488 
489 bool ConvexHullPathFinder::makeCandidateCluster(Eigen::Vector3f& primaryPCA,
490  reco::ClusterParameters& candCluster,
491  reco::HitPairListPtr::iterator firstHitItr,
493  int level) const
494 {
495  std::string indent(level/2, ' ');
496 
497  reco::HitPairListPtr& hitPairListPtr = candCluster.getHitPairListPtr();
498 
499  // size the container...
500  hitPairListPtr.resize(std::distance(firstHitItr,lastHitItr));
501 
502  // and copy the hits into the container
503  std::copy(firstHitItr,lastHitItr,hitPairListPtr.begin());
504 
505  // Will we want to store this cluster?
506  bool keepThisCluster(false);
507 
508  // Must have a valid pca
509  if (completeCandidateCluster(primaryPCA, candCluster, level))
510  {
511  // Recover the new fullPCA
512  reco::PrincipalComponents& newFullPCA = candCluster.getFullPCA();
513 
514  // Need to check if the PCA direction has been reversed
515  Eigen::Vector3f newPrimaryVec(newFullPCA.getEigenVectors()[0][0],newFullPCA.getEigenVectors()[0][1],newFullPCA.getEigenVectors()[0][2]);
516 
517  std::vector<double> eigenValVec = {3. * std::sqrt(newFullPCA.getEigenValues()[0]),
518  3. * std::sqrt(newFullPCA.getEigenValues()[1]),
519  3. * std::sqrt(newFullPCA.getEigenValues()[2])};
520  double cosNewToLast = std::abs(primaryPCA.dot(newPrimaryVec));
521  double eigen2To1Ratio = eigenValVec[2] / eigenValVec[1];
522  double eigen1To0Ratio = eigenValVec[1] / eigenValVec[0];
523 
524  // Create a rough cut intended to tell us when we've reached the land of diminishing returns
525 // if (candCluster.getBestEdgeList().size() > 4 && cosNewToLast > 0.25 && eigen2To1Ratio < 0.9 && eigen2To0Ratio > 0.001)
526  if (candCluster.getConvexHull().getConvexHullEdgeList().size() > 4 && cosNewToLast > 0.25 && eigen2To1Ratio > 0.01 && eigen2To1Ratio < 0.99 && eigen1To0Ratio < 0.5)
527  {
528  keepThisCluster = true;
529  }
530  }
531 
532  return keepThisCluster;
533 }
534 
535 bool ConvexHullPathFinder::makeCandidateCluster(Eigen::Vector3f& primaryPCA,
536  reco::ClusterParameters& candCluster,
537  HitOrderTupleList& orderedList,
538  int level) const
539 {
540  std::string indent(level/2, ' ');
541 
542  reco::HitPairListPtr& hitPairListPtr = candCluster.getHitPairListPtr();
543 
544  // Fill the list the old fashioned way...
545  for(const auto& tupleVal : orderedList) hitPairListPtr.emplace_back(std::get<2>(std::get<2>(tupleVal)));
546 
547  // Will we want to store this cluster?
548  bool keepThisCluster(false);
549 
550  // Must have a valid pca
551  if (completeCandidateCluster(primaryPCA, candCluster, level))
552  {
553  // Recover the new fullPCA
554  reco::PrincipalComponents& newFullPCA = candCluster.getFullPCA();
555 
556  // Need to check if the PCA direction has been reversed
557  Eigen::Vector3f newPrimaryVec(newFullPCA.getEigenVectors()[0][0],newFullPCA.getEigenVectors()[0][1],newFullPCA.getEigenVectors()[0][2]);
558 
559  std::vector<double> eigenValVec = {3. * std::sqrt(newFullPCA.getEigenValues()[0]),
560  3. * std::sqrt(newFullPCA.getEigenValues()[1]),
561  3. * std::sqrt(newFullPCA.getEigenValues()[2])};
562  double cosNewToLast = std::abs(primaryPCA.dot(newPrimaryVec));
563  double eigen2To1Ratio = eigenValVec[2] / eigenValVec[1];
564  double eigen1To0Ratio = eigenValVec[1] / eigenValVec[0];
565 
566  // Create a rough cut intended to tell us when we've reached the land of diminishing returns
567  // if (candCluster.getBestEdgeList().size() > 4 && cosNewToLast > 0.25 && eigen2To1Ratio < 0.9 && eigen2To0Ratio > 0.001)
568  if (candCluster.getBestEdgeList().size() > 4 && cosNewToLast > 0.25 && eigen2To1Ratio > 0.01 && eigen2To1Ratio < 0.99 && eigen1To0Ratio < 0.5)
569  {
570  keepThisCluster = true;
571  }
572  }
573 
574  return keepThisCluster;
575 }
576 
577 bool ConvexHullPathFinder::completeCandidateCluster(Eigen::Vector3f& primaryPCA, reco::ClusterParameters& candCluster, int level) const
578 {
579  // First stage of feature extraction runs here
580  fPCAAlg.PCAAnalysis_3D(candCluster.getHitPairListPtr(), candCluster.getFullPCA());
581 
582  // Recover the new fullPCA
583  reco::PrincipalComponents& newFullPCA = candCluster.getFullPCA();
584 
585  // Will we want to store this cluster?
586  bool keepThisCluster(false);
587 
588  // Must have a valid pca
589  if (newFullPCA.getSvdOK())
590  {
591  // Need to check if the PCA direction has been reversed
592  Eigen::Vector3f newPrimaryVec(newFullPCA.getEigenVectors()[0][0],newFullPCA.getEigenVectors()[0][1],newFullPCA.getEigenVectors()[0][2]);
593 
594  // If the PCA's are opposite the flip the axes
595  if (primaryPCA.dot(newPrimaryVec) < 0.)
596  {
598 
599  eigenVectors.resize(3);
600 
601  for(size_t vecIdx = 0; vecIdx < 3; vecIdx++)
602  {
603  eigenVectors[vecIdx].resize(3,0.);
604 
605  eigenVectors[vecIdx][0] = -newFullPCA.getEigenVectors()[vecIdx][0];
606  eigenVectors[vecIdx][1] = -newFullPCA.getEigenVectors()[vecIdx][1];
607  eigenVectors[vecIdx][2] = -newFullPCA.getEigenVectors()[vecIdx][2];
608  }
609 
610  newFullPCA = reco::PrincipalComponents(true,
611  newFullPCA.getNumHitsUsed(),
612  newFullPCA.getEigenValues(),
613  eigenVectors,
614  newFullPCA.getAvePosition(),
615  newFullPCA.getAveHitDoca());
616  }
617 
618  // Set the skeleton PCA to make sure it has some value
619  candCluster.getSkeletonPCA() = candCluster.getFullPCA();
620 
621  // Be sure to compute the oonvex hull surrounding the now broken cluster
622  buildConvexHull(candCluster, level+2);
623 
624  keepThisCluster = true;
625  }
626 
627  return keepThisCluster;
628 }
629 
631 {
632  // Set up container to keep track of edges
633  using HitKinkTuple = std::tuple<int, reco::HitPairListPtr::iterator>;
634  using HitKinkTupleVec = std::vector<HitKinkTuple>;
635 
636  // Set up container to keep track of edges
637  HitKinkTupleVec kinkTupleVec;
638 
639  reco::ConvexHull& convexHull = clusterToBreak.getConvexHull();
640  reco::ConvexHullKinkTupleList& kinkPointList = convexHull.getConvexHullKinkPoints();
641 
642  for(auto& kink : kinkPointList)
643  {
644  const reco::ClusterHit3D* hit3D = std::get<2>(std::get<0>(kink));
645 
646  reco::HitPairListPtr::iterator kinkItr = std::find(hitList.begin(),hitList.end(),hit3D);
647 
648  if (kinkItr == hitList.end()) continue;
649 
650  int numStartToKink = std::distance(hitList.begin(),kinkItr);
651  int numKinkToEnd = std::distance(kinkItr, hitList.end());
652  int minNumHits = std::min(numStartToKink,numKinkToEnd);
653 
654  if (minNumHits > int(fMinTinyClusterSize)) kinkTupleVec.emplace_back(minNumHits,kinkItr);
655  }
656 
657  // No work if the list is empty
658  if (!kinkTupleVec.empty())
659  {
660  std::sort(kinkTupleVec.begin(),kinkTupleVec.end(),[](const auto& left,const auto& right){return std::get<0>(left) > std::get<0>(right);});
661 
662  // Recover the kink point
663  reco::HitPairListPtr::iterator kinkItr = std::get<1>(kinkTupleVec.front());
664 
665  // Set up to split the input cluster
666  outputClusterList.push_back(reco::ClusterParameters());
667 
668  reco::ClusterParameters& clusterParams1 = outputClusterList.back();
669 
670  reco::PrincipalComponents& fullPCA(clusterToBreak.getFullPCA());
671  Eigen::Vector3f fullPrimaryVec(fullPCA.getEigenVectors()[0][0],fullPCA.getEigenVectors()[0][1],fullPCA.getEigenVectors()[0][2]);
672 
673  if (makeCandidateCluster(fullPrimaryVec, clusterParams1, hitList.begin(), kinkItr, level))
674  {
675  outputClusterList.push_back(reco::ClusterParameters());
676 
677  reco::ClusterParameters& clusterParams2 = outputClusterList.back();
678 
679  makeCandidateCluster(fullPrimaryVec, clusterParams2, kinkItr, hitList.end(), level);
680  }
681 
682  // If we did not make 2 clusters then be sure to clear the output list
683  if (outputClusterList.size() != 2) outputClusterList.clear();
684  }
685 
686  return !outputClusterList.empty();
687 }
688 
690 {
691  // Set up container to keep track of edges
692  KinkTupleVec kinkTupleVec;
693 
694  reco::ConvexHull& convexHull = clusterToBreak.getConvexHull();
695  reco::ProjectedPointList& pointList = convexHull.getProjectedPointList();
696  reco::ConvexHullKinkTupleList& kinkPointList = convexHull.getConvexHullKinkPoints();
697 
698  for(auto& kink : kinkPointList)
699  {
700  // Make an instance of the vec value to avoid copying if we can...
701  kinkTupleVec.push_back(KinkTuple());
702 
703  KinkTuple& kinkTuple = kinkTupleVec.back();
704 
705  std::get<1>(kinkTuple) = kink;
706 
707  // Recover vectors, want them pointing away from intersection point
708  Eigen::Vector2f firstEdge = -std::get<1>(kink);
709  HitOrderTupleList& firstList = std::get<2>(kinkTuple);
710  HitOrderTupleList& secondList = std::get<3>(kinkTuple);
711 
712  orderHitsAlongEdge(pointList, std::get<0>(kink), firstEdge, firstList);
713 
714  if (firstList.size() > fMinTinyClusterSize)
715  {
716  Eigen::Vector2f secondEdge = std::get<2>(kink);
717 
718  orderHitsAlongEdge(pointList, std::get<0>(kink), secondEdge, secondList);
719 
720  if (secondList.size() > fMinTinyClusterSize)
721  std::get<0>(kinkTuple) = std::min(firstList.size(),secondList.size());
722  }
723 
724  // Special handling...
725  if (firstList.size() + secondList.size() > pointList.size())
726  {
727  if (firstList.size() > secondList.size()) pruneHitOrderTupleLists(firstList,secondList);
728  else pruneHitOrderTupleLists(secondList,firstList);
729 
730  std::get<0>(kinkTuple) = std::min(firstList.size(),secondList.size());
731  }
732 
733  if (std::get<0>(kinkTuple) < int(fMinTinyClusterSize)) kinkTupleVec.pop_back();
734  }
735 
736  // No work if the list is empty
737  if (!kinkTupleVec.empty())
738  {
739  // If more than one then want the kink with the most elements both sizes
740  std::sort(kinkTupleVec.begin(),kinkTupleVec.end(),[](const auto& left,const auto& right){return std::get<0>(left) > std::get<0>(right);});
741 
742  // Recover the kink point
743  KinkTuple& kinkTuple = kinkTupleVec.front();
744 
745  // Set up to split the input cluster
746  outputClusterList.push_back(reco::ClusterParameters());
747 
748  reco::ClusterParameters& clusterParams1 = outputClusterList.back();
749 
750  reco::PrincipalComponents& fullPCA(clusterToBreak.getFullPCA());
751  Eigen::Vector3f fullPrimaryVec(fullPCA.getEigenVectors()[0][0],fullPCA.getEigenVectors()[0][1],fullPCA.getEigenVectors()[0][2]);
752 
753  if (makeCandidateCluster(fullPrimaryVec, clusterParams1, std::get<2>(kinkTuple), level))
754  {
755  outputClusterList.push_back(reco::ClusterParameters());
756 
757  reco::ClusterParameters& clusterParams2 = outputClusterList.back();
758 
759  makeCandidateCluster(fullPrimaryVec, clusterParams2, std::get<3>(kinkTuple), level);
760  }
761 
762  // If we did not make 2 clusters then be sure to clear the output list
763  if (outputClusterList.size() != 2) outputClusterList.clear();
764  }
765 
766  return !outputClusterList.empty();
767 }
768 
770  const reco::ProjectedPoint& point,
771  const Eigen::Vector2f& edge,
772  HitOrderTupleList& orderedList) const
773 {
774  // Use the input kink point as the start point of the edge
775  Eigen::Vector2f kinkPos(std::get<0>(point),std::get<1>(point));
776 
777  // Loop over the input hits
778  for (const auto& hit : hitList)
779  {
780  // Now we need to calculate the doca and poca...
781  // Start by getting this hits position
782  Eigen::Vector2f hitPos(std::get<0>(hit),std::get<1>(hit));
783 
784  // Form a TVector from this to the cluster average position
785  Eigen::Vector2f hitToKinkVec = hitPos - kinkPos;
786 
787  // With this we can get the arclength to the doca point
788  float arcLenToPoca = hitToKinkVec.dot(edge);
789 
790  // Require the hit to not be past the kink point
791  if (arcLenToPoca < 0.) continue;
792 
793  // Get the coordinates along the axis for this point
794  Eigen::Vector2f pocaPos = kinkPos + arcLenToPoca * edge;
795 
796  // Now get doca and poca
797  Eigen::Vector2f pocaPosToHitPos = hitPos - pocaPos;
798  float pocaToAxis = pocaPosToHitPos.norm();
799 
800  std::cout << "-- arcLenToPoca: " << arcLenToPoca << ", doca: " << pocaToAxis << std::endl;
801 
802  orderedList.emplace_back(arcLenToPoca,pocaToAxis,hit);
803  }
804 
805  // Sort the list in order of ascending distance from the kink point
806  orderedList.sort([](const auto& left,const auto& right){return std::get<0>(left) < std::get<0>(right);});
807 
808  return;
809 }
810 
812 {
813  // Assume the first list is the short one, so we loop through the elements of that list..
814  HitOrderTupleList::iterator shortItr = shortList.begin();
815 
816  while(shortItr != shortList.end())
817  {
818  // Recover the search key
819  const reco::ClusterHit3D* hit3D = std::get<2>(std::get<2>(*shortItr));
820 
821  // Ok, find the corresponding point in the other list...
822  HitOrderTupleList::iterator longItr = std::find_if(longList.begin(),longList.end(),[&hit3D](const auto& elem){return hit3D == std::get<2>(std::get<2>(elem));});
823 
824  if (longItr != longList.end())
825  {
826  if (std::get<1>(*longItr) < std::get<1>(*shortItr))
827  {
828  shortItr = shortList.erase(shortItr);
829  }
830  else
831  {
832  longItr = longList.erase(longItr);
833  shortItr++;
834  }
835  }
836  else shortItr++;
837  }
838 
839 
840  return;
841 }
842 
844 {
845  // Set up container to keep track of edges
846  using DistEdgeTuple = std::tuple<float, const reco::EdgeTuple*>;
847  using DistEdgeTupleVec = std::vector<DistEdgeTuple>;
848 
849  DistEdgeTupleVec distEdgeTupleVec;
850 
851  reco::ProjectedPointList::const_iterator extremePointListItr = clusterToBreak.getConvexHull().getConvexHullExtremePoints().begin();
852 
853  const reco::ClusterHit3D* firstEdgeHit = std::get<2>(*extremePointListItr++);
854  const reco::ClusterHit3D* secondEdgeHit = std::get<2>(*extremePointListItr);
855  Eigen::Vector3f edgeVec(secondEdgeHit->getPosition()[0] - firstEdgeHit->getPosition()[0],
856  secondEdgeHit->getPosition()[1] - firstEdgeHit->getPosition()[1],
857  secondEdgeHit->getPosition()[2] - firstEdgeHit->getPosition()[2]);
858  double edgeLen = edgeVec.norm();
859 
860  // normalize it
861  edgeVec.normalize();
862 
863 
864  // Now loop through all the edges and search for the furthers point
865  for(const auto& edge : clusterToBreak.getBestEdgeList())
866  {
867  const reco::ClusterHit3D* nextEdgeHit = std::get<0>(edge); // recover the first point
868 
869  // Create vector to this point from the longest edge
870  Eigen::Vector3f hitToEdgeVec(nextEdgeHit->getPosition()[0] - firstEdgeHit->getPosition()[0],
871  nextEdgeHit->getPosition()[1] - firstEdgeHit->getPosition()[1],
872  nextEdgeHit->getPosition()[2] - firstEdgeHit->getPosition()[2]);
873 
874  // Get projection
875  float hitProjection = hitToEdgeVec.dot(edgeVec);
876 
877  // Require that the point is really "opposite" the longest edge
878  if (hitProjection > 0. && hitProjection < edgeLen)
879  {
880  Eigen::Vector3f distToHitVec = hitToEdgeVec - hitProjection * edgeVec;
881  float distToHit = distToHitVec.norm();
882 
883  distEdgeTupleVec.emplace_back(distToHit,&edge);
884  }
885  }
886 
887  std::sort(distEdgeTupleVec.begin(),distEdgeTupleVec.end(),[](const auto& left,const auto& right){return std::get<0>(left) > std::get<0>(right);});
888 
889  reco::PrincipalComponents& fullPCA(clusterToBreak.getFullPCA());
890  Eigen::Vector3f fullPrimaryVec(fullPCA.getEigenVectors()[0][0],fullPCA.getEigenVectors()[0][1],fullPCA.getEigenVectors()[0][2]);
891 
892  // Get a temporary container to hol
893  float usedDefectDist(0.);
894 
895  for(const auto& distEdgeTuple : distEdgeTupleVec)
896  {
897  const reco::EdgeTuple& edgeTuple = *std::get<1>(distEdgeTuple);
898  const reco::ClusterHit3D* edgeHit = std::get<0>(edgeTuple);
899 
900  usedDefectDist = std::get<0>(distEdgeTuple);
901 
902  // Now find the hit identified above as furthest away
903  reco::HitPairListPtr::iterator vertexItr = std::find(hitList.begin(),hitList.end(),edgeHit);
904 
905  // Make sure enough hits either side, otherwise we just keep the current cluster
906  if (vertexItr == hitList.end() || std::distance(hitList.begin(),vertexItr) < int(fMinTinyClusterSize) || std::distance(vertexItr,hitList.end()) < int(fMinTinyClusterSize)) continue;
907 
908  outputClusterList.push_back(reco::ClusterParameters());
909 
910  reco::ClusterParameters& clusterParams1 = outputClusterList.back();
911 
912  if (makeCandidateCluster(fullPrimaryVec, clusterParams1, hitList.begin(), vertexItr, level))
913  {
914  outputClusterList.push_back(reco::ClusterParameters());
915 
916  reco::ClusterParameters& clusterParams2 = outputClusterList.back();
917 
918  if (makeCandidateCluster(fullPrimaryVec, clusterParams2, vertexItr, hitList.end(), level))
919  {
920  if (fFillHistograms)
921  {
922  fSubMaxDefect->Fill(std::get<0>(distEdgeTupleVec.front()), 1.);
923  fSubUsedDefect->Fill(usedDefectDist, 1.);
924  }
925  break;
926  }
927  }
928 
929  // If here then we could not make two valid clusters and so we try again
930  outputClusterList.clear();
931  }
932 
933  return !outputClusterList.empty();
934 }
935 
937 {
938  reco::HitPairListPtr::iterator vertexItr = hitList.begin();
939 
940  std::advance(vertexItr, hitList.size()/2);
941 
942  outputClusterList.push_back(reco::ClusterParameters());
943 
944  reco::ClusterParameters& clusterParams1 = outputClusterList.back();
945 
946  reco::PrincipalComponents& fullPCA(clusterToBreak.getFullPCA());
947  Eigen::Vector3f fullPrimaryVec(fullPCA.getEigenVectors()[0][0],fullPCA.getEigenVectors()[0][1],fullPCA.getEigenVectors()[0][2]);
948 
949  if (makeCandidateCluster(fullPrimaryVec, clusterParams1, hitList.begin(), vertexItr, level))
950  {
951  outputClusterList.push_back(reco::ClusterParameters());
952 
953  reco::ClusterParameters& clusterParams2 = outputClusterList.back();
954 
955  makeCandidateCluster(fullPrimaryVec, clusterParams2, vertexItr, hitList.end(), level);
956  }
957 
958  if (outputClusterList.size() != 2) outputClusterList.clear();
959 
960  return !outputClusterList.empty();
961 }
962 
964 {
965  // Idea here is to scan the input hit list (assumed ordered along the current PCA) and look for "large" gaps
966  // Here a gap is determined when the hits were ordered by their distance along the primary PCA to their doca to it.
967 
968  // Loop through the input hit list and keep track of first hit of largest gap
969  reco::HitPairListPtr::iterator bigGapHitItr = hitList.begin();
970  float biggestGap = 0.;
971 
972  const reco::ClusterHit3D* lastHit = *hitList.begin();
973 
974  for(reco::HitPairListPtr::iterator hitItr = hitList.begin(); hitItr != hitList.end(); hitItr++)
975  {
976  const reco::ClusterHit3D* currentHit = *hitItr;
977 
978  float currentGap = std::abs(currentHit->getArclenToPoca() - lastHit->getArclenToPoca());
979 
980  if (currentGap > biggestGap)
981  {
982  bigGapHitItr = hitItr;
983  biggestGap = currentGap;
984  }
985 
986  lastHit = currentHit;
987  }
988 
989  // Require some minimum gap size...
990  if (biggestGap > 2.)
991  {
992  outputClusterList.push_back(reco::ClusterParameters());
993 
994  reco::ClusterParameters& clusterParams1 = outputClusterList.back();
995 
996  reco::PrincipalComponents& fullPCA(clusterToBreak.getFullPCA());
997  Eigen::Vector3f fullPrimaryVec(fullPCA.getEigenVectors()[0][0],fullPCA.getEigenVectors()[0][1],fullPCA.getEigenVectors()[0][2]);
998 
999  if (makeCandidateCluster(fullPrimaryVec, clusterParams1, hitList.begin(), bigGapHitItr, level))
1000  {
1001  outputClusterList.push_back(reco::ClusterParameters());
1002 
1003  reco::ClusterParameters& clusterParams2 = outputClusterList.back();
1004 
1005  makeCandidateCluster(fullPrimaryVec, clusterParams2, bigGapHitItr, hitList.end(), level);
1006  }
1007 
1008  if (outputClusterList.size() != 2) outputClusterList.clear();
1009  }
1010 
1011  return !outputClusterList.empty();
1012 }
1013 
1014 
1015 void ConvexHullPathFinder::buildConvexHull(reco::ClusterParameters& clusterParameters, int level) const
1016 {
1017  // set an indention string
1018  std::string minuses(level/2, '-');
1019  std::string indent(level/2, ' ');
1020 
1021  indent += minuses;
1022 
1023  // The plan is to build the enclosing 2D polygon around the points in the PCA plane of most spread for this cluster
1024  // To do so we need to start by building a list of 2D projections onto the plane of most spread...
1025  reco::PrincipalComponents& pca = clusterParameters.getFullPCA();
1026 
1027  // Recover the parameters from the Principal Components Analysis that we need to project and accumulate
1028  Eigen::Vector3f pcaCenter(pca.getAvePosition()[0],pca.getAvePosition()[1],pca.getAvePosition()[2]);
1029  Eigen::Vector3f planeVec0(pca.getEigenVectors()[0][0],pca.getEigenVectors()[0][1],pca.getEigenVectors()[0][2]);
1030  Eigen::Vector3f planeVec1(pca.getEigenVectors()[1][0],pca.getEigenVectors()[1][1],pca.getEigenVectors()[1][2]);
1031  Eigen::Vector3f pcaPlaneNrml(pca.getEigenVectors()[2][0],pca.getEigenVectors()[2][1],pca.getEigenVectors()[2][2]);
1032 
1033  // Let's get the rotation matrix from the standard coordinate system to the PCA system.
1034  Eigen::Matrix3f rotationMatrix;
1035 
1036  rotationMatrix << planeVec0(0), planeVec0(1), planeVec0(2),
1037  planeVec1(0), planeVec1(1), planeVec1(2),
1038  pcaPlaneNrml(0), pcaPlaneNrml(1), pcaPlaneNrml(2);
1039 
1040  reco::ConvexHull& convexHull = clusterParameters.getConvexHull();
1041  reco::ProjectedPointList& pointList = convexHull.getProjectedPointList();
1042 
1043  // Loop through hits and do projection to plane
1044  for(const auto& hit3D : clusterParameters.getHitPairListPtr())
1045  {
1046  Eigen::Vector3f pcaToHitVec(hit3D->getPosition()[0] - pcaCenter(0),
1047  hit3D->getPosition()[1] - pcaCenter(1),
1048  hit3D->getPosition()[2] - pcaCenter(2));
1049  Eigen::Vector3f pcaToHit = rotationMatrix * pcaToHitVec;
1050 
1051  pointList.emplace_back(dcel2d::Point(pcaToHit(0),pcaToHit(1),hit3D));
1052  }
1053 
1054  // Sort the point vec by increasing x, then increase y
1055  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);});
1056 
1057  // containers for finding the "best" hull...
1058  std::vector<ConvexHull> convexHullVec;
1059  std::vector<reco::ProjectedPointList> rejectedListVec;
1060  bool increaseDepth(pointList.size() > 3);
1061  float lastArea(std::numeric_limits<float>::max());
1062 
1063  while(increaseDepth)
1064  {
1065  // Get another convexHull container
1066  convexHullVec.push_back(ConvexHull(pointList, fConvexHullKinkAngle, fConvexHullMinSep));
1067  rejectedListVec.push_back(reco::ProjectedPointList());
1068 
1069  const ConvexHull& convexHull = convexHullVec.back();
1070  reco::ProjectedPointList& rejectedList = rejectedListVec.back();
1071  const reco::ProjectedPointList& convexHullPoints = convexHull.getConvexHull();
1072 
1073  increaseDepth = false;
1074 
1075  if (convexHull.getConvexHullArea() > 0.)
1076  {
1077  if (convexHullVec.size() < 2 || convexHull.getConvexHullArea() < 0.8 * lastArea)
1078  {
1079  for(auto& point : convexHullPoints)
1080  {
1081  pointList.remove(point);
1082  rejectedList.emplace_back(point);
1083  }
1084  lastArea = convexHull.getConvexHullArea();
1085 // increaseDepth = true;
1086  }
1087  }
1088  }
1089 
1090  // do we have a valid convexHull?
1091  while(!convexHullVec.empty() && convexHullVec.back().getConvexHullArea() < 0.5)
1092  {
1093  convexHullVec.pop_back();
1094  rejectedListVec.pop_back();
1095  }
1096 
1097  // If we found the convex hull then build edges around the region
1098  if (!convexHullVec.empty())
1099  {
1100  size_t nRejectedTotal(0);
1101  reco::HitPairListPtr hitPairListPtr = clusterParameters.getHitPairListPtr();
1102 
1103  for(const auto& rejectedList : rejectedListVec)
1104  {
1105  nRejectedTotal += rejectedList.size();
1106 
1107  for(const auto& rejectedPoint : rejectedList)
1108  {
1109  if (convexHullVec.back().findNearestDistance(rejectedPoint) > 0.5)
1110  hitPairListPtr.remove(std::get<2>(rejectedPoint));
1111  }
1112  }
1113 
1114  // Now add "edges" to the cluster to describe the convex hull (for the display)
1115  reco::Hit3DToEdgeMap& edgeMap = convexHull.getConvexHullEdgeMap();
1116  reco::EdgeList& edgeList = convexHull.getConvexHullEdgeList();
1117 
1118  reco::ProjectedPoint lastPoint = convexHullVec.back().getConvexHull().front();
1119 
1120  for(auto& curPoint : convexHullVec.back().getConvexHull())
1121  {
1122  if (curPoint == lastPoint) continue;
1123 
1124  const reco::ClusterHit3D* lastPoint3D = std::get<2>(lastPoint);
1125  const reco::ClusterHit3D* curPoint3D = std::get<2>(curPoint);
1126 
1127  float distBetweenPoints = (curPoint3D->getPosition()[0] - lastPoint3D->getPosition()[0]) * (curPoint3D->getPosition()[0] - lastPoint3D->getPosition()[0])
1128  + (curPoint3D->getPosition()[1] - lastPoint3D->getPosition()[1]) * (curPoint3D->getPosition()[1] - lastPoint3D->getPosition()[1])
1129  + (curPoint3D->getPosition()[2] - lastPoint3D->getPosition()[2]) * (curPoint3D->getPosition()[2] - lastPoint3D->getPosition()[2]);
1130 
1131  distBetweenPoints = std::sqrt(distBetweenPoints);
1132 
1133  reco::EdgeTuple edge(lastPoint3D,curPoint3D,distBetweenPoints);
1134 
1135  edgeMap[lastPoint3D].push_back(edge);
1136  edgeMap[curPoint3D].push_back(edge);
1137  edgeList.emplace_back(edge);
1138 
1139  lastPoint = curPoint;
1140  }
1141 
1142  // Store the "extreme" points
1143  const ConvexHull::PointList& extremePoints = convexHullVec.back().getExtremePoints();
1144  reco::ProjectedPointList& extremePointList = convexHull.getConvexHullExtremePoints();
1145 
1146  for(const auto& point : extremePoints) extremePointList.push_back(point);
1147 
1148  // Store the "kink" points
1149  const reco::ConvexHullKinkTupleList& kinkPoints = convexHullVec.back().getKinkPoints();
1150  reco::ConvexHullKinkTupleList& kinkPointList = convexHull.getConvexHullKinkPoints();
1151 
1152  for(const auto& kink : kinkPoints) kinkPointList.push_back(kink);
1153  }
1154 
1155  return;
1156 }
1157 
1158 float ConvexHullPathFinder::closestApproach(const Eigen::Vector3f& P0,
1159  const Eigen::Vector3f& u0,
1160  const Eigen::Vector3f& P1,
1161  const Eigen::Vector3f& u1,
1162  Eigen::Vector3f& poca0,
1163  Eigen::Vector3f& poca1) const
1164 {
1165  // Technique is to compute the arclength to each point of closest approach
1166  Eigen::Vector3f w0 = P0 - P1;
1167  float a(1.);
1168  float b(u0.dot(u1));
1169  float c(1.);
1170  float d(u0.dot(w0));
1171  float e(u1.dot(w0));
1172  float den(a * c - b * b);
1173 
1174  float arcLen0 = (b * e - c * d) / den;
1175  float arcLen1 = (a * e - b * d) / den;
1176 
1177  poca0 = P0 + arcLen0 * u0;
1178  poca1 = P1 + arcLen1 * u1;
1179 
1180  return (poca0 - poca1).norm();
1181 }
1182 
1183 float ConvexHullPathFinder::findConvexHullEndPoints(const reco::EdgeList& convexHull, const reco::ClusterHit3D* first3D, const reco::ClusterHit3D* last3D) const
1184 {
1185  float largestDistance(0.);
1186 
1187  // Note that edges are vectors and that the convex hull edge list will be ordered
1188  // 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
1189  // meaning that the dot product of the two edges becomes negative.
1190  reco::EdgeList::const_iterator firstEdgeItr = convexHull.begin();
1191 
1192  while(firstEdgeItr != convexHull.end())
1193  {
1194  reco::EdgeList::const_iterator nextEdgeItr = firstEdgeItr;
1195 
1196 // Eigen::Vector2f firstEdgeVec(std::get<3>(*firstEdgeItr),std::get<);
1197 // Eigen::Vector2f lastPrimaryVec(lastPCA.getEigenVectors()[0][0],lastPCA.getEigenVectors()[0][1],lastPCA.getEigenVectors()[0][2]);
1198 // float cosToLast = newPrimaryVec.dot(lastPrimaryVec);
1199 
1200  while(++nextEdgeItr != convexHull.end())
1201  {
1202 
1203  }
1204  }
1205 
1206  return largestDistance;
1207 }
1208 
1210 } // namespace lar_cluster3d
size_t fMinTinyClusterSize
Minimum size for a "tiny" cluster.
float fConvexHullMinSep
Min hit separation to conisder in convex hull.
void PCAAnalysis_calc3DDocas(const reco::HitPairListPtr &hitPairVector, const reco::PrincipalComponents &pca) const
bool breakClusterAtBigGap(reco::ClusterParameters &, reco::HitPairListPtr &, reco::ClusterParametersList &, int level) const
bool getSvdOK() const
Definition: Cluster3D.h:226
constexpr auto const & right(const_AssnsIter< L, R, D, Dir > const &a, const_AssnsIter< L, R, D, Dir > const &b)
Definition: AssnsIter.h:112
#define DEFINE_ART_CLASS_TOOL(tool)
Definition: ToolMacros.h:45
std::tuple< int, reco::ConvexHullKinkTuple, HitOrderTupleList, HitOrderTupleList > KinkTuple
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.
bool breakClusterInHalf(reco::ClusterParameters &, reco::HitPairListPtr &, reco::ClusterParametersList &, int level) const
intermediate_table::iterator iterator
std::tuple< float, float, reco::ProjectedPoint > HitOrderTuple
std::list< ProjectedPoint > ProjectedPointList
Definition: Cluster3D.h:334
Float_t den
Definition: plot.C:37
Declaration of signal hit object.
reco::PrincipalComponents & getSkeletonPCA()
Definition: Cluster3D.h:455
std::list< Point > PointList
The list of the projected points.
Definition: ConvexHull.h:32
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.
reco::EdgeList & getBestEdgeList()
Definition: Cluster3D.h:458
const float * getAvePosition() const
Definition: Cluster3D.h:230
int getNumHitsUsed() const
Definition: Cluster3D.h:227
reco::HitPairListPtr & getHitPairListPtr()
Definition: Cluster3D.h:453
Cluster finding and building.
TFileDirectory mkdir(std::string const &dir, std::string const &descr="")
void configure(fhicl::ParameterSet const &pset) override
IClusterModAlg interface class definiton.
ClusterParametersList & daughterList()
Definition: Cluster3D.h:427
Implements a ConvexHull for use in clustering.
std::list< EdgeTuple > EdgeList
Definition: Cluster3D.h:326
Int_t max
Definition: plot.C:27
intermediate_table::const_iterator const_iterator
reco::PrincipalComponents & getFullPCA()
Definition: Cluster3D.h:454
reco::Hit3DToEdgeMap & getConvexHullEdgeMap()
Definition: Cluster3D.h:363
std::tuple< double, double, const reco::ClusterHit3D * > Point
Definitions used by the VoronoiDiagram algorithm.
Definition: DCEL.h:34
reco::ProjectedPointList & getConvexHullExtremePoints()
Definition: Cluster3D.h:365
parameter set interface
reco::ConvexHullKinkTupleList & getConvexHullKinkPoints()
Definition: Cluster3D.h:366
Define a container for working with the convex hull.
Definition: Cluster3D.h:341
T get(std::string const &key) const
Definition: ParameterSet.h:231
std::string indent(std::size_t const i)
void pruneHitOrderTupleLists(HitOrderTupleList &, HitOrderTupleList &) const
ConvexHullPathFinder(const fhicl::ParameterSet &)
Constructor.
Float_t d
Definition: plot.C:237
std::list< ConvexHullKinkTuple > ConvexHullKinkTupleList
Definition: Cluster3D.h:336
std::tuple< const reco::ClusterHit3D *, const reco::ClusterHit3D *, double > EdgeTuple
Definition: Cluster3D.h:325
std::list< const reco::ClusterHit3D * > HitPairListPtr
Definition: Cluster3D.h:317
float getConvexHullArea() const
recover the area of the convex hull
Definition: ConvexHull.h:76
std::vector< std::vector< float > > EigenVectors
Definition: Cluster3D.h:209
This provides an art tool interface definition for 3D Cluster algorithms.
The geometry of one entire detector, as served by art.
Definition: Geometry.h:110
const PointList & getConvexHull() const
recover the list of convex hull vertices
Definition: ConvexHull.h:56
const float * getPosition() const
Definition: Cluster3D.h:147
Detector simulation of raw signals on wires.
ConvexHull class definiton.
Definition: ConvexHull.h:25
This header file defines the interface to a principal components analysis designed to be used within ...
Encapsulate the geometry of a wire.
void buildConvexHull(reco::ClusterParameters &clusterParameters, int level=0) const
std::unique_ptr< lar_cluster3d::IClusterAlg > fClusterAlg
Algorithm to do 3D space point clustering.
bool makeCandidateCluster(Eigen::Vector3f &, reco::ClusterParameters &, reco::HitPairListPtr::iterator, reco::HitPairListPtr::iterator, int) const
constexpr auto const & left(const_AssnsIter< L, R, D, Dir > const &a, const_AssnsIter< L, R, D, Dir > const &b)
Definition: AssnsIter.h:104
reco::ConvexHull & getConvexHull()
Definition: Cluster3D.h:459
T * make(ARGS...args) const
bool breakClusterByMaxDefect(reco::ClusterParameters &, reco::HitPairListPtr &, reco::ClusterParametersList &, int level) const
Utility object to perform functions of association.
void initializeHistograms(art::TFileDirectory &) override
Interface for initializing histograms if they are desired Note that the idea is to put hisgtograms in...
const float * getEigenValues() const
Definition: Cluster3D.h:228
Encapsulate the construction of a single detector plane.
TDirectory * dir
Definition: macro.C:5
const float getAveHitDoca() const
Definition: Cluster3D.h:231
Int_t min
Definition: plot.C:26
float closestApproach(const Eigen::Vector3f &, const Eigen::Vector3f &, const Eigen::Vector3f &, const Eigen::Vector3f &, Eigen::Vector3f &, Eigen::Vector3f &) const
void orderHitsAlongEdge(const reco::ProjectedPointList &, const reco::ProjectedPoint &, const Eigen::Vector2f &, HitOrderTupleList &) const
float fConvexHullKinkAngle
Angle to declare a kink in convex hull calc.
MaybeLogger_< ELseverityLevel::ELsev_success, false > LogDebug
This provides an art tool interface definition for 3D Cluster algorithms.
float getArclenToPoca() const
Definition: Cluster3D.h:157
bool breakClusterByKinksTrial(reco::ClusterParameters &, reco::HitPairListPtr &, reco::ClusterParametersList &, int level) const
bool breakClusterByKinks(reco::ClusterParameters &, reco::HitPairListPtr &, reco::ClusterParametersList &, int level) const
std::unordered_map< const reco::ClusterHit3D *, reco::EdgeList > Hit3DToEdgeMap
Definition: Cluster3D.h:328
void ModifyClusters(reco::ClusterParametersList &) const override
Scan an input collection of clusters and modify those according to the specific implementing algorith...
Float_t e
Definition: plot.C:34
reco::ProjectedPointList & getProjectedPointList()
Definition: Cluster3D.h:362
std::pair< MinMaxPoints, MinMaxPoints > MinMaxPointPair
reco::EdgeList & getConvexHullEdgeList()
Definition: Cluster3D.h:364
float findConvexHullEndPoints(const reco::EdgeList &, const reco::ClusterHit3D *, const reco::ClusterHit3D *) const
std::tuple< float, float, const reco::ClusterHit3D * > ProjectedPoint
Projected coordinates and pointer to hit.
Definition: Cluster3D.h:333
art framework interface to geometry description
std::list< ClusterParameters > ClusterParametersList
Definition: Cluster3D.h:381
const EigenVectors & getEigenVectors() const
Definition: Cluster3D.h:229
std::pair< reco::ProjectedPoint, reco::ProjectedPoint > MinMaxPoints
bool completeCandidateCluster(Eigen::Vector3f &, reco::ClusterParameters &, int) const