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