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