/*
==== Author:

Relja Arandjelovic (relja@robots.ox.ac.uk)
Visual Geometry Group,
Department of Engineering Science
University of Oxford

==== Copyright:

The library belongs to Relja Arandjelovic and the University of Oxford.
No usage or redistribution is allowed without explicit permission.
*/

#include "register_images.h"

#include <algorithm>
#include <math.h>
#include <vector>

#include <boost/thread.hpp>
#include <boost/filesystem.hpp>

#include <Magick++.h>

#include "ellipse.h"
#include "feat_getter.h"
#include "feat_standard.h"
#include "putative.h"
#include "det_ransac.h"
#include "macros.h"

//---- stuff for parallel feature extraction

class featWorker {

    public:

        featWorker( featGetter *aFeatGetterObj, const char aFn[], double aXl, double aXu, double aYl, double aYu, uint32_t &aNumFeats, std::vector<ellipse> &aRegions, float *&aDescs ) : featGetterObj(aFeatGetterObj), fn(aFn), xl(aXl), xu(aXu), yl(aYl), yu(aYu), numFeats(&aNumFeats), regions(&aRegions), descs(&aDescs)
            {}

    void
        operator()(){
            featGetterObj->getFeats( fn,
                            static_cast<uint32_t>(xl), static_cast<uint32_t>(xu),
                            static_cast<uint32_t>(yl), static_cast<uint32_t>(yu),
                            *numFeats, *regions, *descs );
        }

    private:
        featGetter *featGetterObj;
        const char *fn;
        double xl, xu, yl, yu;
        uint32_t *numFeats;
        std::vector<ellipse> *regions;
        float **descs;

};

//----

void
registerImages::registerFromGuess(
        sameRandomUint32 const &sameRandomObj,
        const char image_fn1[], const char image_fn2[],
        double xl, double xu, double yl, double yu,
        homography &Hinit,
        const char outFn1[], const char outFn2[], const char outFn2t[],
        const char *fullSizeFn1, const char *fullSizeFn2 ) {

    static const double expandOutBy= 0.1;
    featGetter *featGetterObj= new featGetter_standard( "hesaff-rootsift" );

    bool fullSizeExist= false;
    if (fullSizeFn1!=NULL || fullSizeFn2!=NULL)
        fullSizeExist= true;
    if (fullSizeFn1==NULL) fullSizeFn1= image_fn1;
    if (fullSizeFn2==NULL) fullSizeFn2= image_fn2;

    Magick::Image im1; im1.read( fullSizeFn1 );
    Magick::Image im2; im2.read( fullSizeFn2 );
    Magick::Image im2t;

    if (fullSizeExist) {
        // modify Hinit to account for scale change
        Magick::Image imSmall1; imSmall1.read( image_fn1 );
        Magick::Image imSmall2; imSmall2.read( image_fn2 );
        double sc1w= static_cast<double>(im1.columns())/imSmall1.columns();
        double sc2w= static_cast<double>(im2.columns())/imSmall2.columns();
        double sc1h= static_cast<double>(im1.rows())/imSmall1.rows();
        double sc2h= static_cast<double>(im2.rows())/imSmall2.rows();
        double sc21w= sc2w/sc1w, sc21h= sc2h/sc1h;
        Hinit.H[0]*= sc21w;
        Hinit.H[1]*= sc21w;
        Hinit.H[2]*= sc2w;
        Hinit.H[3]*= sc21h;
        Hinit.H[4]*= sc21h;
        Hinit.H[5]*= sc2h;
        xl*= sc1w; xu*= sc1w;
        yl*= sc1h; yu*= sc1h;
    }

    homography H= Hinit;

    uint32_t numFeats1, numFeats2, bestNInliers;
    float *descs1, *descs2;
    std::vector<ellipse> regions1, regions2;

    boost::thread *thread1, *thread2;

    // compute RootSIFT: image 1

    thread1= new boost::thread( featWorker( featGetterObj, fullSizeFn1, xl, xu, yl, yu, numFeats1, regions1, descs1 ) );

    bool firstGo= true, extractFinished1= false;
    uint32_t loopNum_= 0;

    boost::filesystem::path tmpdir = boost::filesystem::temp_directory_path();
    boost::filesystem::path tmpfn = tmpdir / boost::filesystem::unique_path("vise_rr_register_%%%%%%%%%%%%%%%%.jpg");
    std::string fullSizeFn2_t = tmpfn.string();

    matchesType inlierInds;

    while (1){

        if (!firstGo){

            // compute RootSIFT: image 2

            thread2= new boost::thread( featWorker( featGetterObj, fullSizeFn2_t.c_str(), xl, xu, yl, yu, numFeats2, regions2, descs2 ) );

            // wait for feature extraction of image 1 to finish
            if (!extractFinished1){
                thread1->join();
                delete thread1;
                extractFinished1= true;
            }

            // wait for feature extraction of image 2 to finish
            thread2->join();
            delete thread2;

            // run RANSAC

            homography Hnew;

            detRansac::matchDesc(
                sameRandomObj,
                bestNInliers,
                descs1, regions1,
                descs2, regions2,
                featGetterObj->numDims(),
                loopNum_>1?1.0:5.0, 0.0, 1000.0, static_cast<uint32_t>(4),
                true, 0.81f, 100.0f,
                &Hnew, &inlierInds
                );

            bool success= bestNInliers>9;

            if (!success)
                break;

            // apply new H to current H (i.e. H= H * Hnew)
            {
                double Happlied[9];
                for (int i= 0; i<3; ++i)
                    for (int j=0; j<3; ++j)
                        Happlied[i*3+j]= H.H[i*3  ] * Hnew.H[  j] +
                                         H.H[i*3+1] * Hnew.H[3+j] +
                                         H.H[i*3+2] * Hnew.H[6+j];
                H.set(Happlied);
            }

        }

        H.normLast();

        // im2 -> im1 transformation
        double Hinv[9];
        H.getInverse(Hinv);
        homography::normLast(Hinv);

        // warp image 2 into image 1
        im2t= im2;
        // AffineProjection(sx, rx, ry, sy, tx, ty) <=> H=[sx, ry, tx; sy, rx, ty; 0 0 1]
        double MagickAffine[6]={Hinv[0],Hinv[3],Hinv[1],Hinv[4],Hinv[2],Hinv[5]};
        im2t.virtualPixelMethod(Magick::BlackVirtualPixelMethod);
        im2t.distort(Magick::AffineProjectionDistortion, 6, MagickAffine, false);

        firstGo= false;

        ++loopNum_;
        if (loopNum_>1)
            break;

        im2t.write( fullSizeFn2_t.c_str() );
    }

    delete []descs1;
    delete []descs2;
    boost::filesystem::remove( fullSizeFn2_t );

    // draw resulting images

    double xl_= xl, xu_= xu, yl_= yl, yu_= yu;
    double dw_= expandOutBy*(xu-xl), dh_= expandOutBy*(yu-yl);
    xl_= std::max(0.0, xl_-dw_/2);
    yl_= std::max(0.0, yl_-dh_/2);
    xu_= std::min(static_cast<double>(im1.columns() ), xu_+dw_/2);
    yu_= std::min(static_cast<double>(im1.rows()), yu_+dh_/2);
    Magick::Geometry cropRect1(xu_-xl_, yu_-yl_, xl_, yl_);
    double xl2_, xu2_, yl2_, yu2_;
    findBBox2( xl_, xu_, yl_, yu_, H, xl2_, xu2_, yl2_, yu2_, im2.columns(), im2.rows() );
    Magick::Geometry cropRect2(xu2_-xl2_, yu2_-yl2_, xl2_, yl2_);

    im1.crop( cropRect1 );
    im1.write( outFn1 );
    im2.crop( cropRect2 );
    im2.write( outFn2 );
    im2t.crop( cropRect1 );
    im2t.write( outFn2t );

    delete featGetterObj;
}

void
registerImages::registerFromQuery(
        query const &query_obj,
        const char inFn1[], uint32_t docID2,
        datasetAbs const &datasetObj,
        spatialRetriever const &spatialRetriever_obj,
        const char outFn1[], const char outFn2[], const char outFn2t[],
        const char *fullSizeFn1, const char *fullSizeFn2 ) {
    homography H;
    std::vector< std::pair<ellipse,ellipse> > matches;
    spatialRetriever_obj.get_matches_using_query( query_obj, docID2, H, matches );
    std::string image_fn1= inFn1;
    if (query_obj.isInternal)
        image_fn1= datasetObj.getFn( query_obj.docID );
    std::string image_fn2= datasetObj.getFn( docID2 );

    registerImages::registerFromGuess( *(spatialRetriever_obj.getSameRandom()), image_fn1.c_str(), image_fn2.c_str(), query_obj.xl, query_obj.xu, query_obj.yl, query_obj.yu, H, outFn1, outFn2, outFn2t, fullSizeFn1, fullSizeFn2 );

}

void
registerImages::findBBox2( double xl, double xu, double yl, double yu, homography const &H, double &xl2, double &xu2, double &yl2, double &yu2, uint32_t w2, uint32_t h2 ){

    ASSERT( fabs(H.H[8]-1.0)<1e-5 );

    xl2= 10000; xu2= -10000; yl2= 10000; yu2= -10000;
    double x_, y_;

    homography::affTransform(H.H, xl, yl, x_, y_);
    xl2= std::min(xl2,x_); xu2= std::max(xu2,x_); yl2= std::min(yl2,y_); yu2= std::max(yu2,y_);

    homography::affTransform(H.H, xl, yu, x_, y_);
    xl2= std::min(xl2,x_); xu2= std::max(xu2,x_); yl2= std::min(yl2,y_); yu2= std::max(yu2,y_);

    homography::affTransform(H.H, xu, yl, x_, y_);
    xl2= std::min(xl2,x_); xu2= std::max(xu2,x_); yl2= std::min(yl2,y_); yu2= std::max(yu2,y_);

    homography::affTransform(H.H, xu, yu, x_, y_);
    xl2= std::min(xl2,x_); xu2= std::max(xu2,x_); yl2= std::min(yl2,y_); yu2= std::max(yu2,y_);

    xl2= std::max(0.0,xl2);
    yl2= std::max(0.0,yl2);
    xu2= std::min(xu2,static_cast<double>(w2));
    yu2= std::min(yu2,static_cast<double>(h2));

}


// --------------------------------------------------------------------------
//                   OpenMS -- Open-Source Mass Spectrometry
// --------------------------------------------------------------------------
// Copyright The OpenMS Team -- Eberhard Karls University Tuebingen,
// ETH Zurich, and Freie Universitaet Berlin 2002-2020.
//
// This software is released under a three-clause BSD license:
//  * Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
//  * Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
//  * Neither the name of any author or any participating institution
//    may be used to endorse or promote products derived from this software
//    without specific prior written permission.
// For a full list of authors, refer to the file AUTHORS.
// --------------------------------------------------------------------------
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL ANY OF THE AUTHORS OR THE CONTRIBUTING
// INSTITUTIONS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// --------------------------------------------------------------------------
// $Maintainer: Hannes Roest $
// $Authors: Hannes Roest $
// --------------------------------------------------------------------------

#include <OpenMS/ANALYSIS/OPENSWATH/SONARScoring.h>

#include <OpenMS/ANALYSIS/OPENSWATH/DIAScoring.h>
#include <OpenMS/ANALYSIS/OPENSWATH/DIAHelper.h>
#include <OpenMS/OPENSWATHALGO/ALGO/StatsHelpers.h>

#include <OpenMS/MATH/STATISTICS/LinearRegression.h>
#include <OpenMS/MATH/STATISTICS/StatisticFunctions.h>

#include <OpenMS/OPENSWATHALGO/ALGO/Scoring.h>

#include <boost/cast.hpp>

// #define DEBUG_SONAR

namespace OpenMS
{
  SONARScoring::SONARScoring() :
    DefaultParamHandler("SONARScoring")
  {
    defaults_.setValue("dia_extraction_window", 0.05, "DIA extraction window in Th or ppm.");
    defaults_.setMinFloat("dia_extraction_window", 0.0);
    defaults_.setValue("dia_extraction_unit", "Th", "DIA extraction window unit");
    defaults_.setValidStrings("dia_extraction_unit", {"Th","ppm"});
    defaults_.setValue("dia_centroided", "false", "Use centroided DIA data.");
    defaults_.setValidStrings("dia_centroided", {"true","false"});

    // write defaults into Param object param_
    defaultsToParam_();
  }

  void SONARScoring::updateMembers_()
  {
    dia_extract_window_ = (double)param_.getValue("dia_extraction_window");
    dia_extraction_ppm_ = param_.getValue("dia_extraction_unit") == "ppm";
    dia_centroided_ = param_.getValue("dia_centroided").toBool();
  }

  void SONARScoring::computeXCorr_(std::vector<std::vector<double> >& sonar_profiles,
                                   double& xcorr_coelution_score, double& xcorr_shape_score) const
  {
    /// Cross Correlation array
    typedef OpenSwath::Scoring::XCorrArrayType XCorrArrayType;
    /// Cross Correlation matrix
    typedef std::vector<std::vector<XCorrArrayType> > XCorrMatrixType;

    XCorrMatrixType xcorr_matrix;
    xcorr_matrix.resize(sonar_profiles.size());
    for (std::size_t i = 0; i < sonar_profiles.size(); i++)
    {
      xcorr_matrix[i].resize(sonar_profiles.size());
      for (std::size_t j = i; j < sonar_profiles.size(); j++)
      {
        // compute normalized cross correlation
        xcorr_matrix[i][j] = OpenSwath::Scoring::normalizedCrossCorrelation(
                                  sonar_profiles[i], sonar_profiles[j], boost::numeric_cast<int>(sonar_profiles[i].size()), 1);
      }
    }

    // coelution (lag score)
    std::vector<int> deltas;
    for (std::size_t i = 0; i < xcorr_matrix.size(); i++)
    {
      for (std::size_t  j = i; j < xcorr_matrix.size(); j++)
      {
        // first is the lag value, should be an int
        deltas.push_back(std::abs(OpenSwath::Scoring::xcorrArrayGetMaxPeak(xcorr_matrix[i][j])->first));
      }
    }

    {
      OpenSwath::mean_and_stddev msc;
      msc = std::for_each(deltas.begin(), deltas.end(), msc);
      double deltas_mean = msc.mean();
      double deltas_stdv = msc.sample_stddev();
      xcorr_coelution_score = deltas_mean + deltas_stdv;
    }

    // shape score (intensity)
    std::vector<double> intensities;
    for (std::size_t i = 0; i < xcorr_matrix.size(); i++)
    {
      for (std::size_t j = i; j < xcorr_matrix.size(); j++)
      {
        // second is the Y value (intensity)
        intensities.push_back(OpenSwath::Scoring::xcorrArrayGetMaxPeak(xcorr_matrix[i][j])->second);
      }
    }
    {
      OpenSwath::mean_and_stddev msc;
      msc = std::for_each(intensities.begin(), intensities.end(), msc);
      xcorr_shape_score = msc.mean();
    }
  }

  void SONARScoring::computeSonarScores(OpenSwath::IMRMFeature* imrmfeature,
                                        const std::vector<OpenSwath::LightTransition> & transitions,
                                        const std::vector<OpenSwath::SwathMap>& swath_maps,
                                        OpenSwath_Scores & scores) const
  {
    if (transitions.empty()) {return;}

    double precursor_mz = transitions[0].getPrecursorMZ();

#ifdef DEBUG_SONAR
    std::ofstream debug_file;
    debug_file.open("debug_sonar_profiles.tsv",  std::fstream::in | std::fstream::out | std::fstream::app);

    String native_id = 0;
    if (transitions.size() > 0)
    {
      native_id = transitions[0].getNativeID();
    }
    debug_file << native_id << "\t" << imrmfeature->getRT() << "\tcentr";
    for (Size it = 0; it < swath_maps.size(); it++)
    {
      debug_file << "\t" << (swath_maps[it].lower + swath_maps[it].upper) / 2.0;
    }
    debug_file << "\n";

    std::cout << " doing RT " << imrmfeature->getRT() << " using maps: ";
    for (Size i  = 0; i < swath_maps.size() ; i++)
    {
      std::cout << (swath_maps[i].lower + swath_maps[i].upper) / 2 << " ";
    }
    std::cout << std::endl;

    // idea 1: check the elution profile of each SONAR scan ...
    for (Size kk = 0; kk < imrmfeature->getNativeIDs().size(); kk++)
    {
      std::vector<double> rt;
      imrmfeature->getFeature(imrmfeature->getNativeIDs()[kk])->getRT(rt);
    }
#endif

    // idea 2: check the SONAR profile (e.g. in the dimension of) of the best scan (RT apex)
    double RT = imrmfeature->getRT();

    // Aggregate sonar profiles (for each transition)
    std::vector<std::vector<double> > sonar_profiles;
    std::vector<double> sn_score;
    std::vector<double> diff_score;
    std::vector<double> trend_score;
    std::vector<double> rsq_score;
    std::vector<double> mz_median_score;
    std::vector<double> mz_stdev_score;
    for (Size k = 0; k < transitions.size(); k++)
    {
      String native_id = transitions[k].getNativeID();

      // Gather profiles across all SONAR maps
      std::vector<double> sonar_profile;
      std::vector<double> sonar_mz_profile;
      std::vector<bool> signal_exp;
      for (Size swath_idx = 0; swath_idx < swath_maps.size(); swath_idx++)
      {
        OpenSwath::SpectrumAccessPtr swath_map = swath_maps[swath_idx].sptr;

        bool expect_signal = false;
        if (swath_maps[swath_idx].ms1) {continue;} // skip MS1
        if (precursor_mz > swath_maps[swath_idx].lower && precursor_mz < swath_maps[swath_idx].upper)
        {
          expect_signal = true;
        }

        // find closest scan for current SONAR map (by retention time)
        std::vector<std::size_t> indices = swath_map->getSpectraByRT(RT, 0.0);
        if (indices.empty() )  {continue;}
        int closest_idx = boost::numeric_cast<int>(indices[0]);
        if (indices[0] != 0 &&
            std::fabs(swath_map->getSpectrumMetaById(boost::numeric_cast<int>(indices[0]) - 1).RT - RT) <
            std::fabs(swath_map->getSpectrumMetaById(boost::numeric_cast<int>(indices[0])).RT - RT))
        {
          closest_idx--;
        }
        OpenSwath::SpectrumPtr spectrum_ = swath_map->getSpectrumById(closest_idx);

        // integrate intensity within that scan
        double left = transitions[k].getProductMZ();
        double right = transitions[k].getProductMZ();
        if (dia_extraction_ppm_)
        {
          left -= left * dia_extract_window_ / 2e6;
          right += right * dia_extract_window_ / 2e6;
        }
        else
        {
          left -= dia_extract_window_ / 2.0;
          right += dia_extract_window_ / 2.0;
        }
        double mz, intensity;
        DIAHelpers::integrateWindow(spectrum_, left, right, mz, intensity, dia_centroided_);

        sonar_profile.push_back(intensity);
        sonar_mz_profile.push_back(mz);
        signal_exp.push_back(expect_signal);
      }
      sonar_profiles.push_back(sonar_profile);

#ifdef DEBUG_SONAR
      std::cout << " transition " << native_id << " at " << RT << " will analyze with " << swath_maps.size() << " maps" << std::endl;
      debug_file << native_id << "\t" << imrmfeature->getRT() << "\tint";
      for (Size it = 0; it < sonar_profile.size(); it++)
      {
        debug_file << "\t" << sonar_profile[it];
      }
      debug_file << "\n";
      debug_file << native_id << "\t" << imrmfeature->getRT() << "\tmz";
      for (Size it = 0; it < sonar_mz_profile.size(); it++)
      {
        debug_file << "\t" << sonar_mz_profile[it];
      }
      debug_file << "\n";
#endif

      // Analyze profiles
      std::vector<double> sonar_profile_pos;
      std::vector<double> sonar_mz_profile_pos;
      std::vector<double> sonar_profile_neg;
      std::vector<double> sonar_mz_profile_neg;
      for (Size it = 0; it < sonar_profile.size(); it++)
      {
        if (signal_exp[it])
        {
          sonar_profile_pos.push_back(sonar_profile[it]);
          sonar_mz_profile_pos.push_back(sonar_mz_profile[it]);
        }
        else
        {
          sonar_profile_neg.push_back(sonar_profile[it]);
          sonar_mz_profile_neg.push_back(sonar_mz_profile[it]);
        }
      }

      // try to find diff between first and last
      double sonar_trend = 1.0;
      if (sonar_profile_pos.size() > 1)
      {
        double int_end = sonar_profile_pos[sonar_profile_pos.size()-1] + sonar_profile_pos[sonar_profile_pos.size()-2];
        double int_start = sonar_profile_pos[0] + sonar_profile_pos[1];
        if (int_end > 0.0)
        {
          sonar_trend = int_start / int_end;
        }
        else
        {
          sonar_trend = 0.0;
        }
      }

      // try to find R^2 of a linear regression (optimally, there is no trend)
      std::vector<double> xvals;
      for (Size pr_idx = 0; pr_idx < sonar_profile_pos.size(); pr_idx++) {xvals.push_back(pr_idx);}
      double rsq = OpenSwath::cor_pearson( xvals.begin(), xvals.end(), sonar_profile_pos.begin() );
      if (boost::math::isnan(rsq)) rsq = 0.0; // check for nan

      // try to find largest diff
      double sonar_largediff = 0.0;
      for (Size pr_idx = 0; pr_idx < sonar_profile_pos.size()-1; pr_idx++)
      {
        double diff = std::fabs(sonar_profile_pos[pr_idx] - sonar_profile_pos[pr_idx+1]);
        if (diff > sonar_largediff)
        {
          sonar_largediff = diff;
        }
      }

      double sonar_sn = 1.0;
      double pos_med = 1.0;
      // from here on, its not sorted any more !!
      if (!sonar_profile_pos.empty() && !sonar_profile_neg.empty())
      {
        double neg_med;

        pos_med = Math::median(sonar_profile_pos.begin(), sonar_profile_pos.end());
        neg_med = Math::median(sonar_profile_neg.begin(), sonar_profile_neg.end());

        // compute the relative difference between the medians (or if the
        // medians are zero, compute the difference to the max element)
        if (neg_med > 0.0)
        {
          sonar_sn = pos_med / neg_med;
        }
        else if (*std::max_element(sonar_profile_neg.begin(), sonar_profile_neg.end()) > 0.0)
        {
          sonar_sn = pos_med / *std::max_element(sonar_profile_neg.begin(), sonar_profile_neg.end());
        }

      }

      double median_mz = 0.0;
      double mz_stdev = -1.0;
      if (!sonar_mz_profile_pos.empty())
      {
        median_mz = Math::median(sonar_mz_profile_pos.begin(), sonar_mz_profile_pos.end());

        double sum = std::accumulate(sonar_mz_profile_pos.begin(), sonar_mz_profile_pos.end(), 0.0);
        double mean = sum / sonar_mz_profile_pos.size();

        double sq_sum = std::inner_product(sonar_mz_profile_pos.begin(), sonar_mz_profile_pos.end(), sonar_mz_profile_pos.begin(), 0.0);
        double stdev = std::sqrt(sq_sum / sonar_mz_profile_pos.size() - mean * mean);

        mz_stdev = stdev;
      }

#ifdef DEBUG_SONAR
      std::cout << " computed SN: " << sonar_sn  <<  "(from " << pos_med << " and neg " << neg_med <<  ")"
        << " large diff: "  << sonar_largediff << " trend " << sonar_trend << std::endl;
#endif
      sn_score.push_back(sonar_sn);
      diff_score.push_back(sonar_largediff / pos_med);
      trend_score.push_back(sonar_trend);
      rsq_score.push_back(rsq);

      mz_median_score.push_back(median_mz);
      mz_stdev_score.push_back(mz_stdev);
    }

    double xcorr_coelution_score, xcorr_shape_score;
    computeXCorr_(sonar_profiles, xcorr_coelution_score, xcorr_shape_score);

    double sn_av = std::accumulate(sn_score.begin(), sn_score.end(), 0.0) / sn_score.size();
    double diff_av = std::accumulate(diff_score.begin(), diff_score.end(), 0.0) / diff_score.size();
    double trend_av = std::accumulate(trend_score.begin(), trend_score.end(), 0.0) / trend_score.size();
    double rsq_av = std::accumulate(rsq_score.begin(), rsq_score.end(), 0.0) / rsq_score.size();

    //double mz_median = std::accumulate(mz_median_score.begin(), mz_median_score.end(), 0.0) / mz_median_score.size();
    //double mz_stdev = std::accumulate(mz_stdev_score.begin(), mz_stdev_score.end(), 0.0) / mz_stdev_score.size();

    scores.sonar_sn = sn_av;
    scores.sonar_diff = diff_av;
    scores.sonar_trend = trend_av;
    scores.sonar_rsq = rsq_av;
    scores.sonar_lag = xcorr_coelution_score;
    scores.sonar_shape = xcorr_shape_score;

#ifdef DEBUG_SONAR
    debug_file.close();
#endif
  }

}


//
//
using namespace std;
#include "EventDisplay/src/DataInterface.h"

#include "CLHEP/Vector/LorentzVector.h"
#include "CLHEP/Vector/Rotation.h"
#include "CalorimeterGeom/inc/DiskCalorimeter.hh"
#include "CalorimeterGeom/inc/Calorimeter.hh"
#include "ConditionsService/inc/CrvParams.hh"
#include "ConditionsService/inc/ConditionsHandle.hh"
#include "CosmicRayShieldGeom/inc/CosmicRayShield.hh"
#include "DetectorSolenoidGeom/inc/DetectorSolenoid.hh"
#include "EventDisplay/src/Cube.h"
#include "EventDisplay/src/Cylinder.h"
#include "EventDisplay/src/Cone.h"
#include "EventDisplay/src/EventDisplayFrame.h"
#include "EventDisplay/src/Hexagon.h"
#include "EventDisplay/src/Straw.h"
#include "EventDisplay/src/Track.h"
#include "EventDisplay/src/TrackColorSelector.h"
#include "EventDisplay/src/dict_classes/ComponentInfo.h"
#include "EventDisplay/src/dict_classes/EventDisplayViewSetup.h"
#include "GeometryService/inc/GeomHandle.hh"
#include "GeometryService/inc/DetectorSystem.hh"
#include "HepPID/ParticleName.hh"
#include "HepPDT/ParticleData.hh"
#include "MCDataProducts/inc/PhysicalVolumeInfoMultiCollection.hh"
#include "MCDataProducts/inc/MCTrajectoryCollection.hh"
#include "MCDataProducts/inc/SimParticlePtrCollection.hh"
#include "MCDataProducts/inc/StepPointMCCollection.hh"
#include "Mu2eUtilities/inc/PhysicalVolumeMultiHelper.hh"
#include "ConfigTools/inc/SimpleConfig.hh"
#include "RecoDataProducts/inc/KalSeed.hh"
#include "RecoDataProducts/inc/CrvDigiCollection.hh"
#include "RecoDataProducts/inc/CaloHit.hh"
#include "RecoDataProducts/inc/StrawHitCollection.hh"
#include "RecoDataProducts/inc/StrawHitFlagCollection.hh"
#include "RecoDataProducts/inc/TrkExtTrajCollection.hh"
#include "RecoDataProducts/inc/TrkExtTraj.hh"
#include "RecoDataProducts/inc/TrkExtTrajCollection.hh"
#include "StoppingTargetGeom/inc/StoppingTarget.hh"
#include "StoppingTargetGeom/inc/TargetFoil.hh"
#include "TrkReco/inc/TrkUtilities.hh"
#include "TrackerGeom/inc/Tracker.hh"
#include "art/Framework/Principal/Run.h"
#include "cetlib/map_vector.h"
#include <TAxis3D.h>
#include <TGFrame.h>
#include <TGeoVolume.h>
#include <TMath.h>
#include <TView.h>
#include <TGraphErrors.h>
#include <TF1.h>

#include <boost/shared_array.hpp>

using namespace CLHEP;
#include "RecoDataProducts/inc/KalRepCollection.hh"
#include "BTrkData/inc/TrkStrawHit.hh"
#include "BTrk/KalmanTrack/KalRep.hh"
#include "BTrk/BaBar/ExternalInfo.hh"

namespace mu2e_eventdisplay
{

DataInterface::DataInterface(EventDisplayFrame *mainframe):
              _geometrymanager(nullptr),_topvolume(nullptr),_mainframe(mainframe)
{
    _minPoints=0;
    _minTime=NAN;
    _maxTime=NAN;
    _minMomentum=0;
    _showElectrons=true;
    _showMuons=true;
    _showGammas=true;
    _showNeutrinos=true;
    _showNeutrons=true;
    _showOthers=true;

    _particleInfo = make_unique<mu2e::ParticleInfo>();
    ExternalInfo::set(_particleInfo.get());
}

DataInterface::~DataInterface()
{
  removeAllComponents();
}

void DataInterface::startComponents()
{
//  removeNonGeometryComponents();
}

void DataInterface::updateComponents(double time, boost::shared_ptr<ContentSelector> contentSelector)
{
  std::vector<boost::shared_ptr<Track> >::const_iterator track;
  for(track=_tracks.begin(); track!=_tracks.end(); track++)
  {
    (*track)->setFilter(_minPoints, _minTime, _maxTime, _minMomentum,
                        _showElectrons, _showMuons, _showGammas, _showNeutrinos, _showNeutrons, _showOthers);
    (*track)->update(time);
  }

  const mu2e::StrawHitFlagCollection *hitFlagCollection=contentSelector->getStrawHitFlagCollection();
  std::vector<boost::shared_ptr<Straw> >::const_iterator hit; //iterate only over hit straws
  for(hit=_hits.begin(); hit!=_hits.end(); hit++)
  {
    int hitnumber=(*hit)->getHitNumber();
    if(hitFlagCollection!=nullptr)
    {
      //flag collection selected --> show only hits which have certain hit flags
      (*hit)->setFilter(_minTime, _maxTime, true);
      if(hitnumber<static_cast<int>(hitFlagCollection->size()) && hitnumber>=0)
      {
        const mu2e::StrawHitFlag& hitFlag = (*hitFlagCollection)[hitnumber];
        if(hitFlag.hasAnyProperty(_hitFlagSetting))
        {
          (*hit)->setFilter(_minTime, _maxTime, false);
        }
      }
    }
    else
    {
      //no flag collection selected --> show all hits
      (*hit)->setFilter(_minTime, _maxTime, false);
    }
    (*hit)->update(time);
  }

  std::vector<boost::shared_ptr<VirtualShape> >::const_iterator crystalhit; //iterate only over hit crystals
  for(crystalhit=_crystalhits.begin(); crystalhit!=_crystalhits.end(); crystalhit++)
  {
    (*crystalhit)->setFilter(_minTime, _maxTime);
    (*crystalhit)->update(time);
  }

  std::vector<boost::shared_ptr<Cylinder> >::const_iterator driftradius;
  for(driftradius=_driftradii.begin(); driftradius!=_driftradii.end(); driftradius++)
  {
    (*driftradius)->setFilter(_minTime, _maxTime);
    (*driftradius)->update(time);
  }

  std::vector<boost::shared_ptr<Cube> >::const_iterator crvhit;
  for(crvhit=_crvhits.begin(); crvhit!=_crvhits.end(); crvhit++)
  {
    (*crvhit)->setFilter(_minTime, _maxTime);
    (*crvhit)->update(time);
  }
}

void DataInterface::getFilterValues(unsigned int &minPoints, double &minTime, double &maxTime, double &minMomentum,
                                    bool &showElectrons, bool &showMuons, bool &showGammas,
                                    bool &showNeutrinos, bool &showNeutrons, bool &showOthers,
                                    mu2e::StrawHitFlag &hitFlagSetting)
{
    minPoints=_minPoints;
    minTime=_minTime;
    maxTime=_maxTime;
    minMomentum=_minMomentum;
    showElectrons=_showElectrons;
    showMuons=_showMuons;
    showGammas=_showGammas;
    showNeutrinos=_showNeutrinos;
    showNeutrons=_showNeutrons;
    showOthers=_showOthers;
    hitFlagSetting=_hitFlagSetting;
}

void DataInterface::setFilterValues(unsigned int minPoints, double minTime, double maxTime, double minMomentum,
                                    bool showElectrons, bool showMuons, bool showGammas,
                                    bool showNeutrinos, bool showNeutrons, bool showOthers,
                                    mu2e::StrawHitFlag hitFlagSetting)
{
    _minPoints=minPoints;
    _minTime=minTime;
    _maxTime=maxTime;
    _minMomentum=minMomentum;
    _showElectrons=showElectrons;
    _showMuons=showMuons;
    _showGammas=showGammas;
    _showNeutrinos=showNeutrinos;
    _showNeutrons=showNeutrons;
    _showOthers=showOthers;
    _hitFlagSetting=hitFlagSetting;
}

DataInterface::timeminmax DataInterface::getHitsTimeBoundary()
{
  DataInterface::timeminmax toReturn=_hitsTimeMinmax;
  if(_minTime>toReturn.mint) toReturn.mint=_minTime;
  if(_maxTime<toReturn.maxt) toReturn.maxt=_maxTime;
  return toReturn;
}

DataInterface::timeminmax DataInterface::getTracksTimeBoundary()
{
  DataInterface::timeminmax toReturn=_tracksTimeMinmax;
  if(_minTime>toReturn.mint) toReturn.mint=_minTime;
  if(_maxTime<toReturn.maxt) toReturn.maxt=_maxTime;
  return toReturn;
}

void DataInterface::createGeometryManager()
{
  _geometrymanager = new TGeoManager("GeoManager", "GeoManager");
  _geometrymanager->SetVerboseLevel(0);
   TGeoMaterial *matVacuum = new TGeoMaterial("Vacuum", 0,0,0);
   TGeoMedium *Vacuum = new TGeoMedium("Vacuum",1, matVacuum);
  _topvolume = _geometrymanager->MakeBox("TopVolume", Vacuum, 1000, 1000, 1500);
  _geometrymanager->SetTopVolume(_topvolume);
  _geometrymanager->SetTopVisible(false);
  _geometrymanager->CloseGeometry();
  _geometrymanager->SetVisLevel(4);
  _topvolume->SetVisibility(0);
  _topvolume->SetLineColor(0);
  _topvolume->Draw("ogle");
  EventDisplayViewSetup::setup();
}

void DataInterface::fillGeometry()
{
  removeAllComponents();
  createGeometryManager();
  resetBoundaryP(_trackerMinmax);
  resetBoundaryP(_targetMinmax);
  resetBoundaryP(_calorimeterMinmax);
  resetBoundaryP(_tracksMinmax);

  art::ServiceHandle<mu2e::GeometryService> geom;

  const mu2e::SimpleConfig &config = geom->config();
  _detSysOrigin = mu2e::GeomHandle<mu2e::DetectorSystem>()->getOrigin();

  if(geom->hasElement<mu2e::Tracker>())
  {
//Straws
    mu2e::GeomHandle<mu2e::Tracker> tracker;
    const auto& allStraws = tracker->getStraws();
    // for(const auto & elem : allStraws)
    for (size_t i = 0; i<tracker->nStraws(); ++i)
    {
      // const mu2e::Straw& s = elem;
      const mu2e::Straw& s = allStraws[i];
      const CLHEP::Hep3Vector& p = s.getMidPoint();
      const CLHEP::Hep3Vector& d = s.getDirection();
      double x = p.x();
      double y = p.y();
      double z = p.z();
      double theta = d.theta();
      double phi = d.phi();
      double l = s.halfLength();
      int idStraw =  s.id().getStraw();
      int idLayer =  s.id().getLayer();
      int idPanel =  s.id().getPanel();
      int idPlane =  s.id().getPlane();
      int id = s.id().asUint16();

      boost::shared_ptr<ComponentInfo> info(new ComponentInfo());
      std::string c=Form("Straw %i  Layer %i  Panel %i  Plane %i",idStraw,idLayer,idPanel,idPlane);
      info->setName(c.c_str());
      info->setText(0,c.c_str());
      boost::shared_ptr<Straw> shape(new Straw(x,y,z, NAN, theta, phi, l,
                                               _geometrymanager, _topvolume, _mainframe, info, true));
      _components.push_back(shape);
      _straws[id]=shape;
    }

//Support Structure
    double innerRadius=tracker->g4Tracker()->getSupportParams().innerRadius();
    double outerRadius=tracker->g4Tracker()->getSupportParams().outerRadius();
    double zHalfLength=tracker->g4Tracker()->getInnerTrackerEnvelopeParams().zHalfLength();
    findBoundaryP(_trackerMinmax, outerRadius, outerRadius, zHalfLength);
    findBoundaryP(_trackerMinmax, -outerRadius, -outerRadius, -zHalfLength);

    boost::shared_ptr<ComponentInfo> info(new ComponentInfo());
    info->setName("Tracker Support Structure");
    info->setText(0,"Tracker Support Structure");
    info->setText(1,Form("Inner Radius %.f mm  Outer Radius %.f mm",innerRadius/CLHEP::mm,outerRadius/CLHEP::mm));
    info->setText(2,Form("Length %.f mm",2.0*zHalfLength/CLHEP::mm));
    info->setText(3,Form("Center at x: 0 mm, y: 0 mm, z: 0 mm"));
    boost::shared_ptr<Cylinder> shape(new Cylinder(0,0,0, 0,0,0,
                                          zHalfLength,innerRadius,outerRadius, NAN,
                                          _geometrymanager, _topvolume, _mainframe, info, true));
    shape->makeGeometryVisible(true);
    _components.push_back(shape);
    _supportstructures.push_back(shape);

//Envelope
    innerRadius=tracker->g4Tracker()->getInnerTrackerEnvelopeParams().innerRadius();
    outerRadius=tracker->g4Tracker()->getInnerTrackerEnvelopeParams().outerRadius();
    zHalfLength=tracker->g4Tracker()->getInnerTrackerEnvelopeParams().zHalfLength();

    boost::shared_ptr<ComponentInfo> infoEnvelope(new ComponentInfo());
    infoEnvelope->setName("Tracker Envelope");
    infoEnvelope->setText(0,"Tracker Envelope");
    infoEnvelope->setText(1,Form("Inner Radius %.f mm  Outer Radius %.f mm",innerRadius/CLHEP::mm,outerRadius/CLHEP::mm));
    infoEnvelope->setText(2,Form("Length %.f mm",2.0*zHalfLength/CLHEP::mm));
    infoEnvelope->setText(3,Form("Center at x: 0 mm, y: 0 mm, z: 0 mm"));
    boost::shared_ptr<Cylinder> shapeEnvelope(new Cylinder(0,0,0, 0,0,0,
                                                  zHalfLength,innerRadius,outerRadius, NAN,
                                                  _geometrymanager, _topvolume, _mainframe, infoEnvelope, true));
    shapeEnvelope->makeGeometryVisible(true);
    _components.push_back(shapeEnvelope);
    _supportstructures.push_back(shapeEnvelope);
  }

  art::ServiceHandle<mu2e::GeometryService> geoservice;
  if(geoservice->hasElement<mu2e::DetectorSolenoid>())
  {
    mu2e::GeomHandle<mu2e::DetectorSolenoid> ds;

    double innerRadius=ds->rIn1();
    double outerRadius=ds->rOut2();
    double zHalfLength=ds->halfLength();
    double z=ds->position().z() - _detSysOrigin.z();

    boost::shared_ptr<ComponentInfo> infoToyDS(new ComponentInfo());
    infoToyDS->setName("Toy DS");
    infoToyDS->setText(0,"Toy DS");
    infoToyDS->setText(1,Form("Inner Radius %.f mm  Outer Radius %.f mm",innerRadius/CLHEP::mm,outerRadius/CLHEP::mm));
    infoToyDS->setText(2,Form("Length %.f mm",2.0*zHalfLength/CLHEP::mm));
    infoToyDS->setText(3,Form("Center at x: 0 mm, y: 0 mm, z: %.f mm",z/CLHEP::mm));
    boost::shared_ptr<Cylinder> shapeToyDS(new Cylinder(0,0,z, 0,0,0,
                                               zHalfLength,innerRadius,outerRadius, NAN,
                                               _geometrymanager, _topvolume, _mainframe, infoToyDS, true));
    _components.push_back(shapeToyDS);
    _otherstructures.push_back(shapeToyDS);
  }

  if(geom->hasElement<mu2e::StoppingTarget>())
  {
    mu2e::GeomHandle<mu2e::StoppingTarget> target;
    unsigned int n=target->nFoils();
    for(unsigned int i=0; i<n; i++)
    {
      const mu2e::TargetFoil &foil=target->foil(i);
      int id = foil.id();
      double x = foil.centerInDetectorSystem().x();
      double y = foil.centerInDetectorSystem().y();
      double z = foil.centerInDetectorSystem().z();
      double radius = foil.rOut();
      double halfThickness = foil.halfThickness();

      findBoundaryP(_targetMinmax, x+radius, y+radius, z+halfThickness);
      findBoundaryP(_targetMinmax, x-radius, y-radius, z-halfThickness);

      boost::shared_ptr<ComponentInfo> info(new ComponentInfo());
      std::string c=Form("Target Foil ID %i",id);
      info->setName(c.c_str());
      info->setText(0,c.c_str());
      info->setText(1,Form("Outer Radius %.2f mm",radius/CLHEP::mm));
      info->setText(2,Form("Half thickness %.2f mm",halfThickness/CLHEP::mm));
      info->setText(3,Form("Center at x: %.2f mm, y: %.2f mm, z: %.2f mm",x/CLHEP::mm,y/CLHEP::mm,z/CLHEP::mm));
      boost::shared_ptr<Cylinder> shape(new Cylinder(x,y,z, 0,0,0, halfThickness,0,radius, NAN,
                                          _geometrymanager, _topvolume, _mainframe, info, true));
      shape->makeGeometryVisible(true);
      _components.push_back(shape);
      _supportstructures.push_back(shape);
    }
  }

  if(geom->hasElement<mu2e::DiskCalorimeter>())
  {
    mu2e::GeomHandle<mu2e::DiskCalorimeter> calo;

    double diskCaseDZLength      = calo->caloInfo().getDouble("diskCaseZLength")/2.0;
    double diskInnerRingIn       = calo->caloInfo().getDouble("diskInnerRingIn");
    double diskOuterRingOut      = calo->caloInfo().getDouble("diskOuterRingOut");
    double diskOuterRailOut      = diskOuterRingOut + calo->caloInfo().getDouble("diskOutRingEdgeRLength");

    double FPCarbonDZ               = calo->caloInfo().getDouble("FPCarbonZLength")/2.0;
    double FPFoamDZ                 = calo->caloInfo().getDouble("FPFoamZLength")/2.0;
    double FPCoolPipeRadius         = calo->caloInfo().getDouble("FPCoolPipeRadius");
    double pipeRadius               = calo->caloInfo().getDouble("pipeRadius");
    double frontPanelHalfThick      = (2.0*FPCarbonDZ+2.0*FPFoamDZ-pipeRadius+FPCoolPipeRadius)/2.0;
    double holeDZ                   = calo->caloInfo().getDouble("BPHoleZLength")/2.0;

    double crystalDXY            = calo->caloInfo().getDouble("crystalXYLength")/2.0;
    double crystalDZ             = calo->caloInfo().getDouble("crystalZLength")/2.0;
    double crystalFrameDZ        = calo->caloInfo().getDouble("crystalFrameZLength")/2.0;
    double wrapperHalfThick      = calo->caloInfo().getDouble("wrapperThickness")/2.0;
    double wrapperDXY            = crystalDXY + 2.0*wrapperHalfThick;
    double wrapperDZ             = crystalDZ + 2.0*crystalFrameDZ;

    int icrystal=0;
    for(unsigned int idisk=0; idisk<calo->nDisk(); idisk++)
    {
      CLHEP::Hep3Vector diskPos = calo->disk(idisk).geomInfo().origin() - _detSysOrigin;
      diskPos += CLHEP::Hep3Vector(0.0, 0.0, -holeDZ+frontPanelHalfThick);

      findBoundaryP(_calorimeterMinmax, diskPos.x()+diskOuterRailOut, diskPos.y()+diskOuterRailOut, diskPos.z()+diskCaseDZLength);
      findBoundaryP(_calorimeterMinmax, diskPos.x()-diskOuterRailOut, diskPos.y()-diskOuterRailOut, diskPos.z()-diskCaseDZLength);

      boost::shared_ptr<ComponentInfo> diskInfo(new ComponentInfo());
      std::string c=Form("Disk %i",idisk);
      diskInfo->setName(c.c_str());
      diskInfo->setText(0,c.c_str());
      diskInfo->setText(1,Form("Center at x: %.f mm, y: %.f mm, z: %.f mm",diskPos.x(),diskPos.y(),diskPos.z()));
      diskInfo->setText(2,Form("Outer radius: %.f mm, Inner radius: %.f mm, Thickness: %.f mm",diskOuterRailOut,diskInnerRingIn,2.0*diskCaseDZLength));
      boost::shared_ptr<Cylinder> calodisk(new Cylinder(diskPos.x(),diskPos.y(),diskPos.z(),  0,0,0,
                                                        diskCaseDZLength, diskInnerRingIn, diskOuterRailOut, NAN,
                                                        _geometrymanager, _topvolume, _mainframe, diskInfo, true));
      calodisk->makeGeometryVisible(true);
      _components.push_back(calodisk);
      _supportstructures.push_back(calodisk);

      int nCrystalInThisDisk = calo->disk(idisk).nCrystals();
      for(int ic=0; ic<nCrystalInThisDisk; ic++)
      {
        CLHEP::Hep3Vector crystalPosition = calo->disk(idisk).crystal(ic).localPosition();
        crystalPosition.setZ(diskCaseDZLength-wrapperDZ);
        crystalPosition += diskPos;

        boost::shared_ptr<ComponentInfo> info(new ComponentInfo());
        std::string c=Form("Disk %i, Crystal %i",idisk,ic);
        info->setName(c.c_str());
        info->setText(0,c.c_str());
        info->setText(1,Form("Center at x: %.f mm, y: %.f mm, z: %.f mm",crystalPosition.x(),crystalPosition.y(),crystalPosition.z()));
        info->setText(2,Form("XYSize: %.f mm, Thickness: %.f mm",2.0*wrapperDXY,2.0*wrapperDZ));
        boost::shared_ptr<Hexagon> shape(new Hexagon(crystalPosition.x(),crystalPosition.y(),crystalPosition.z(),
                                                     wrapperDXY,wrapperDZ,360, NAN,
                                                     _geometrymanager, _topvolume, _mainframe, info, true));
        _components.push_back(shape);
        _crystals[icrystal]=shape;
        icrystal++;
      }
    }
  }

  //MBS
/*
  if(config.getBool("hasMBS", false))
  {
    double mbsinr[3], mbsoutr[3], mbslen[3], mbsz[3];
    mbsinr[0]  = config.getDouble("mbs.BSTCInnerRadius");
    mbsoutr[0] = config.getDouble("mbs.BSTSOuterRadius");
    mbsinr[1]  = config.getDouble("mbs.BSBSInnerRadius");
    mbsoutr[1] = config.getDouble("mbs.BSTSOuterRadius");
    mbsinr[2]  = config.getDouble("mbs.CLV2InnerRadius");
    mbsoutr[2] = config.getDouble("mbs.CLV2OuterRadius");
    mbslen[0]  = config.getDouble("mbs.BSTCHLength");
    mbslen[1]  = config.getDouble("mbs.BSTSHLength") - mbslen[0];
    mbslen[2]  = config.getDouble("mbs.CLV2HLength");
    double mbsbstsz = config.getDouble("mbs.BSTSZ");
    double mbstotallen = (mbslen[0] + mbslen[1])*2.;
    double mbsstartz = mbsbstsz - mbstotallen/2. - _detSysOrigin.z();
    mbsz[0] = mbsstartz + mbslen[0] ;
    mbsz[1] = mbsstartz + mbslen[0]*2. + mbslen[1];
    mbsz[2] = mbsstartz +mbstotallen - mbslen[2];
    for (unsigned int i = 0 ; i <3 ; ++i)
    {
      boost::shared_ptr<ComponentInfo> infoMBS(new ComponentInfo());
      std::string c=Form(c,"MBS %d", i);
      infoMBS->setName(c.c_str());
      infoMBS->setText(0,c.c_str());
      infoMBS->setText(1,Form("Inner Radius %.f mm  Outer Radius %.f mm",mbsinr[i]/CLHEP::mm,mbsoutr[i]/CLHEP::mm));
      infoMBS->setText(2,Form("Length %.f mm",2.0*mbslen[i]/CLHEP::mm));
      infoMBS->setText(3,Form("Center at x: 0 mm, y: 0 mm, z: %.f mm",mbsz[i]/CLHEP::mm));
      boost::shared_ptr<Cylinder> shapeMBS(new Cylinder(0,0,mbsz[i], 0,0,0,
                                               mbslen[i], mbsinr[i], mbsoutr[i], NAN,
                                               _geometrymanager, _topvolume, _mainframe, infoMBS, true));
      _components.push_back(shapeMBS);
      _mbsstructures.push_back(shapeMBS);
    }
  }
*/
  //MecoStyleProtonAbsorber
  if(config.getBool("hasProtonAbsorber", false))
  {
    if (!config.getBool("protonabsorber.isHelical", false))
    {
      double inr[2], outr[2], thickness, halflength, z;
      outr[0] = config.getDouble("protonabsorber.OutRadius0");
      outr[1] = config.getDouble("protonabsorber.OutRadius1");
      thickness = config.getDouble("protonabsorber.thickness");
      inr[0] = outr[0] - thickness;
      inr[1] = outr[1] - thickness;
      halflength = config.getDouble("protonabsorber.halfLength");
      mu2e::GeomHandle<mu2e::StoppingTarget> target;
      double stoppingtargetlength=target->cylinderLength();
      double stoppingtargetz=target->centerInMu2e().z() - _detSysOrigin.z();
      z = stoppingtargetz + stoppingtargetlength*0.5 + halflength;

      boost::shared_ptr<ComponentInfo> infoMecoStylePA(new ComponentInfo());
      infoMecoStylePA->setName("MECOStyleProtonAbsorber");
      infoMecoStylePA->setText(0,"MECOStyleProtonAbsorber");
      infoMecoStylePA->setText(1,Form("Inner Radius1 %.f mm  Outer Radius1 %.f mm",inr[0]/CLHEP::mm,outr[0]/CLHEP::mm));
      infoMecoStylePA->setText(2,Form("Inner Radius2 %.f mm  Outer Radius2 %.f mm",inr[1]/CLHEP::mm,outr[1]/CLHEP::mm));
      infoMecoStylePA->setText(3,Form("Length %.f mm",2.0*halflength/CLHEP::mm));
      infoMecoStylePA->setText(4,Form("Center at x: 0 mm, y: 0 mm, z: %.f mm",z/CLHEP::mm));
      boost::shared_ptr<Cone> shapePA(new Cone(0,0,z, 0,0,0,
                                                halflength, inr[0], outr[0], inr[1], outr[1],
                                                NAN, _geometrymanager, _topvolume, _mainframe, infoMecoStylePA, true));
      _components.push_back(shapePA);
      _mecostylepastructures.push_back(shapePA);
    }
  }

//CRV
  if( geom->hasElement<mu2e::CosmicRayShield>() )
  {
    mu2e::GeomHandle<mu2e::CosmicRayShield> CosmicRayShieldGeomHandle;
    std::vector<mu2e::CRSScintillatorShield> const& shields = CosmicRayShieldGeomHandle->getCRSScintillatorShields();
    for(std::vector<mu2e::CRSScintillatorShield>::const_iterator ishield=shields.begin(); ishield!=shields.end(); ++ishield)
    {
      mu2e::CRSScintillatorShield const& shield = *ishield;
      std::string const& shieldName = shield.getName();

      mu2e::CRSScintillatorBarDetail const& barDetail = shield.getCRSScintillatorBarDetail();
      double dx=barDetail.getHalfLengths()[0];
      double dy=barDetail.getHalfLengths()[1];
      double dz=barDetail.getHalfLengths()[2];

      int nModules = shield.nModules();
      for (int im = 0; im < nModules; ++im)
      {
        mu2e::CRSScintillatorModule const & module = shield.getModule(im);

        int nLayers = module.nLayers();
        for (int il = 0; il < nLayers; ++il)
        {
          mu2e::CRSScintillatorLayer const & layer = module.getLayer(il);

          int nBars = layer.nBars();
          for (int ib = 0; ib < nBars; ++ib)
          {
            mu2e::CRSScintillatorBar const & bar = layer.getBar(ib);
            int index = bar.index().asInt();
            CLHEP::Hep3Vector barOffset = bar.getPosition() - _detSysOrigin;
            double x=barOffset.x();
            double y=barOffset.y();
            double z=barOffset.z();

            boost::shared_ptr<ComponentInfo> info(new ComponentInfo());
            std::string c=Form("CRV Scintillator %s  module %i  layer %i  bar %i  (index %i)",shieldName.c_str(),im,il,ib, index);
            info->setName(c.c_str());
            info->setText(0,c.c_str());
            info->setText(1,Form("Dimension x: %.f mm, y: %.f mm, z: %.f mm",2.0*dx/CLHEP::mm,2.0*dy/CLHEP::mm,2.0*dz/CLHEP::mm));
            info->setText(2,Form("Center at x: %.f mm, y: %.f mm, z: %.f mm",x/CLHEP::mm,y/CLHEP::mm,z/CLHEP::mm));

            boost::shared_ptr<Cube> shape(new Cube(x,y,z,  dx,dy,dz,  0, 0, 0, NAN,
                                                   _geometrymanager, _topvolume, _mainframe, info, true));
            _components.push_back(shape);
            _crvscintillatorbars[index]=(shape);
          }
        }
      }
    }
  }
}

void DataInterface::makeMuonBeamStopStructuresVisible(bool visible)
{
  std::vector<boost::shared_ptr<VirtualShape> >::const_iterator structure;
  for(structure=_mbsstructures.begin(); structure!=_mbsstructures.end(); structure++)
  {
    (*structure)->makeGeometryVisible(visible);
  }

  //tracks and straws don't have to be pushed into the foreground if the structure is removed
  if(visible) toForeground();
}

void DataInterface::makeMecoStyleProtonAbsorberVisible(bool visible)
{
  std::vector<boost::shared_ptr<Cone> >::const_iterator structure;
  for(structure=_mecostylepastructures.begin(); structure!=_mecostylepastructures.end(); structure++)
  {
    (*structure)->makeGeometryVisible(visible);
  }

  //tracks and straws don't have to be pushed into the foreground if the structure is removed
  if(visible) toForeground();
}

void DataInterface::makeSupportStructuresVisible(bool visible)
{
  std::vector<boost::shared_ptr<VirtualShape> >::const_iterator structure;
  for(structure=_supportstructures.begin(); structure!=_supportstructures.end(); structure++)
  {
    (*structure)->makeGeometryVisible(visible);
  }

  //tracks and straws don't have to be pushed into the foreground if the structure is removed
  if(visible) toForeground();
}

void DataInterface::makeOtherStructuresVisible(bool visible)
{
  std::vector<boost::shared_ptr<VirtualShape> >::const_iterator structure;
  for(structure=_otherstructures.begin(); structure!=_otherstructures.end(); structure++)
  {
    (*structure)->makeGeometryVisible(visible);
  }

  //tracks and straws don't have to be pushed into the foreground if the structure is removed
  if(visible) toForeground();
}

void DataInterface::makeCrvScintillatorBarsVisible(bool visible)
{
  std::map<int, boost::shared_ptr<Cube> >::const_iterator crvbars;
  for(crvbars=_crvscintillatorbars.begin(); crvbars!=_crvscintillatorbars.end(); crvbars++)
  {
    crvbars->second->makeGeometryVisible(visible);
  }

  //tracks and straws don't have to be pushed into the foreground if the structure is removed
  if(visible) toForeground();
}

void DataInterface::toForeground()
{
  std::map<int,boost::shared_ptr<Straw> >::const_iterator straw;
  for(straw=_straws.begin(); straw!=_straws.end(); straw++)
  {
    straw->second->toForeground();
  }

  std::map<int,boost::shared_ptr<VirtualShape> >::const_iterator crystal;
  for(crystal=_crystals.begin(); crystal!=_crystals.end(); crystal++)
  {
    crystal->second->toForeground();
  }

  std::map<int,boost::shared_ptr<Cube> >::const_iterator crvbar;
  for(crvbar=_crvscintillatorbars.begin(); crvbar!=_crvscintillatorbars.end(); crvbar++)
  {
    crvbar->second->toForeground();
  }

  std::vector<boost::shared_ptr<Track> >::const_iterator track;
  for(track=_tracks.begin(); track!=_tracks.end(); track++)
  {
    (*track)->toForeground();
  }
}

void DataInterface::useHitColors(bool hitcolors, bool whitebackground)
{
  double mint=getHitsTimeBoundary().mint;
  double maxt=getHitsTimeBoundary().maxt;
  std::vector<boost::shared_ptr<Straw> >::const_iterator hit;
  for(hit=_hits.begin(); hit!=_hits.end(); hit++)
  {
    double time=(*hit)->getStartTime();
    if(hitcolors)
    {
      int color=TMath::FloorNint(20.0*(time-mint)/(maxt-mint));
      if(color>=20) color=19;
      if(color<=0 || std::isnan(color)) color=0;
      color+=2000;
      (*hit)->setColor(color);
    }
    else (*hit)->setColor(whitebackground?1:0);
  }
  std::vector<boost::shared_ptr<VirtualShape> >::const_iterator crystalhit;
  for(crystalhit=_crystalhits.begin(); crystalhit!=_crystalhits.end(); crystalhit++)
  {
    double time=(*crystalhit)->getStartTime();
    if(hitcolors)
    {
      int color=TMath::FloorNint(20.0*(time-mint)/(maxt-mint));
      if(color>=20) color=19;
      if(color<=0 || std::isnan(color)) color=0;
      color+=2000;
      (*crystalhit)->setColor(color);
    }
    else (*crystalhit)->setColor(whitebackground?1:0);
  }
  std::vector<boost::shared_ptr<Cylinder> >::const_iterator driftradius;
  for(driftradius=_driftradii.begin(); driftradius!=_driftradii.end(); driftradius++)
  {
    double time=(*driftradius)->getStartTime();
    if(hitcolors)
    {
      int color=TMath::FloorNint(20.0*(time-mint)/(maxt-mint));
      if(color>=20) color=19;
      if(color<=0 || std::isnan(color)) color=0;
      color+=2000;
      (*driftradius)->setColor(color);
    }
    else (*driftradius)->setColor(whitebackground?1:0);
  }
  std::vector<boost::shared_ptr<Cube> >::const_iterator crvhit;
  for(crvhit=_crvhits.begin(); crvhit!=_crvhits.end(); crvhit++)
  {
    double time=(*crvhit)->getStartTime();
    if(hitcolors)
    {
      int color=TMath::FloorNint(20.0*(time-mint)/(maxt-mint));
      if(color>=20) color=19;
      if(color<=0 || std::isnan(color)) color=0;
      color+=2000;
      (*crvhit)->setColor(color);
    }
    else (*crvhit)->setColor(whitebackground?1:0);
  }
}

void DataInterface::useTrackColors(boost::shared_ptr<ContentSelector> const &contentSelector, bool trackcolors, bool whitebackground)
{
  std::vector<ContentSelector::trackInfoStruct> selectedTracks=contentSelector->getSelectedTrackNames();
  TrackColorSelector colorSelector(&selectedTracks, whitebackground);
  std::vector<boost::shared_ptr<Track> >::const_iterator track;
  for(track=_tracks.begin(); track!=_tracks.end(); track++)
  {
    if(trackcolors)
    {
      int color=colorSelector.getColor(*track);
      (*track)->setColor(color);
    }
    else (*track)->setColor(whitebackground?kBlack:kWhite);
  }
}

void DataInterface::resetBoundaryT(timeminmax &m)
{
  m.mint=NAN;
  m.maxt=NAN;
}

void DataInterface::resetBoundaryP(spaceminmax &m)
{
  m.minx=NAN;
  m.miny=NAN;
  m.minz=NAN;
  m.maxx=NAN;
  m.maxy=NAN;
  m.maxz=NAN;
}

DataInterface::spaceminmax DataInterface::getSpaceBoundary(bool useTarget, bool useCalorimeter, bool useTracks)
{
  spaceminmax m;
  resetBoundaryP(m);
  findBoundaryP(m, _trackerMinmax.minx, _trackerMinmax.miny, _trackerMinmax.minz);
  findBoundaryP(m, _trackerMinmax.maxx, _trackerMinmax.maxy, _trackerMinmax.maxz);
  if(useTarget)
  {
    findBoundaryP(m, _targetMinmax.minx, _targetMinmax.miny, _targetMinmax.minz);
    findBoundaryP(m, _targetMinmax.maxx, _targetMinmax.maxy, _targetMinmax.maxz);
  }
  if(useCalorimeter)
  {
    findBoundaryP(m, _calorimeterMinmax.minx, _calorimeterMinmax.miny, _calorimeterMinmax.minz);
    findBoundaryP(m, _calorimeterMinmax.maxx, _calorimeterMinmax.maxy, _calorimeterMinmax.maxz);
  }
  if(useTracks)
  {
    findBoundaryP(m, _tracksMinmax.minx, _tracksMinmax.miny, _tracksMinmax.minz);
    findBoundaryP(m, _tracksMinmax.maxx, _tracksMinmax.maxy, _tracksMinmax.maxz);
  }

  if(std::isnan(m.minx)) m.minx=-1000;
  if(std::isnan(m.miny)) m.miny=-1000;
  if(std::isnan(m.minz)) m.minz=-1000;
  if(std::isnan(m.maxx)) m.maxx=1000;
  if(std::isnan(m.maxy)) m.maxy=1000;
  if(std::isnan(m.maxz)) m.maxz=1000;
  return m;
}

void DataInterface::findBoundaryT(timeminmax &m, double t)
{
  if(std::isnan(m.mint) || t<m.mint) m.mint=t;
  if(std::isnan(m.maxt) || t>m.maxt) m.maxt=t;
}

void DataInterface::findBoundaryP(spaceminmax &m, double x, double y, double z)
{
  if(std::isnan(m.minx) || x<m.minx) m.minx=x;
  if(std::isnan(m.miny) || y<m.miny) m.miny=y;
  if(std::isnan(m.minz) || z<m.minz) m.minz=z;
  if(std::isnan(m.maxx) || x>m.maxx) m.maxx=x;
  if(std::isnan(m.maxy) || y>m.maxy) m.maxy=y;
  if(std::isnan(m.maxz) || z>m.maxz) m.maxz=z;
}

void DataInterface::fillEvent(boost::shared_ptr<ContentSelector> const &contentSelector, const mu2e::SimParticleTimeOffset &timeOffsets)
{
  auto const& ptable = mu2e::GlobalConstantsHandle<mu2e::ParticleDataTable>();
  removeNonGeometryComponents();
  if(!_geometrymanager) createGeometryManager();
  resetBoundaryT(_hitsTimeMinmax);
  resetBoundaryT(_tracksTimeMinmax);

  _numberHits=0;
  _numberCrystalHits=0;

  const mu2e::StepPointMCCollection *steppointMChits=contentSelector->getSelectedHitCollection<mu2e::StepPointMCCollection>();
  if(steppointMChits!=nullptr)
  {
    _numberHits=steppointMChits->size();
    std::vector<mu2e::StepPointMC>::const_iterator iter;
    for(iter=steppointMChits->begin(); iter!=steppointMChits->end(); iter++)
    {
      const mu2e::StepPointMC& hit = *iter;
      int sid = hit.strawId().asUint16();
      int trackid = hit.trackId().asInt();
      double time = timeOffsets.timeWithOffsetsApplied(hit);
      double energy = hit.eDep();
      std::map<int,boost::shared_ptr<Straw> >::iterator straw=_straws.find(sid);
      if(straw!=_straws.end() && !std::isnan(time))
      {
        double previousStartTime=straw->second->getStartTime();
        if(std::isnan(previousStartTime))
        {
          findBoundaryT(_hitsTimeMinmax, time);  //is it Ok to exclude all following hits from the time window?
          straw->second->setStartTime(time);
          straw->second->getComponentInfo()->setText(1,Form("hit time(s): %gns",time/CLHEP::ns));
          straw->second->getComponentInfo()->setText(2,Form("deposited energy(s): %geV",energy/CLHEP::eV));
          straw->second->getComponentInfo()->setText(3,Form("track id(s): %i",trackid));
          _hits.push_back(straw->second);
        }
        else
        {
          straw->second->getComponentInfo()->expandLine(1,Form("%gns",time/CLHEP::ns));
          straw->second->getComponentInfo()->expandLine(2,Form("%geV",energy/CLHEP::eV));
          straw->second->getComponentInfo()->expandLine(3,Form("%i",trackid));
        }
      }
    }
  }

  const mu2e::StrawHitCollection *strawhits=contentSelector->getSelectedHitCollection<mu2e::StrawHitCollection>();
  if(strawhits!=nullptr)
  {
    _numberHits=strawhits->size();
    std::vector<mu2e::StrawHit>::const_iterator iter;
    int hitnumber=0;
    for(iter=strawhits->begin(); iter!=strawhits->end(); iter++, hitnumber++)
    {
      const mu2e::StrawHit& hit = *iter;
      int sid  = hit.strawId().asUint16();
      double time = hit.time();
      double dt = hit.dt();
      double energy = hit.energyDep();
      std::map<int,boost::shared_ptr<Straw> >::iterator straw=_straws.find(sid);
      if(straw!=_straws.end() && !std::isnan(time))
      {
        double previousStartTime=straw->second->getStartTime();
        if(std::isnan(previousStartTime))
        {
          findBoundaryT(_hitsTimeMinmax, time);  //is it Ok to exclude all following hits from the time window?
          straw->second->setStartTime(time);
          straw->second->setHitNumber(hitnumber);
          straw->second->getComponentInfo()->setText(1,Form("hit time(s): %gns",time/CLHEP::ns));
          straw->second->getComponentInfo()->setText(2,Form("deposited energy(s): %geV",energy/CLHEP::eV));
          straw->second->getComponentInfo()->setText(3,Form("hit time interval(s): %gns",dt/CLHEP::ns));
          _hits.push_back(straw->second);
        }
        else
        {
          straw->second->getComponentInfo()->expandLine(1,Form("%gns",time/CLHEP::ns));
          straw->second->getComponentInfo()->expandLine(2,Form("%geV",energy/CLHEP::eV));
          straw->second->getComponentInfo()->expandLine(3,Form("%gns",dt/CLHEP::ns));
        }
      }
    }
  }

  const mu2e::KalRepCollection *kalRepHits=contentSelector->getSelectedHitCollection<mu2e::KalRepCollection>();
  if(kalRepHits!=nullptr)
  {
    boost::shared_ptr<TGraphErrors> residualGraph(new TGraphErrors());
    residualGraph->SetTitle("Residual Graph");

    for(unsigned int i=0; i<kalRepHits->size(); i++)
    {
      const KalRep &particle = kalRepHits->at(i);
      TrkHitVector const& hots = particle.hitVector();
      if(hots.size() > 0)
      {
        _numberHits+=hots.size();
        for(auto iter=hots.begin(); iter!=hots.end(); iter++)
        {
          const TrkHit *hitOnTrack = *iter;
          const mu2e::TrkStrawHit* strawHit = dynamic_cast<const mu2e::TrkStrawHit*>(hitOnTrack);
          if(strawHit)
          {
            int    sid=strawHit->straw().id().asUint16();
            double time = strawHit->time();
            double hitT0 = strawHit->hitT0()._t0; //this is the time the hit "arrived at the straw"
                                              //don't know what the other times are
            double strawtime = strawHit->comboHit().time();
            double driftRadius = strawHit->driftRadius();
            const HepPoint &p=strawHit->hitTraj()->position(strawHit->hitLen());
            double theta = strawHit->straw().getDirection().theta();
            double phi = strawHit->straw().getDirection().phi();

            double residual, residualError;
            if(strawHit->resid(residual, residualError))
            {
              int n=residualGraph->GetN();
              residualGraph->SetPoint(n,p.z(),residual);
              residualGraph->SetPointError(n,0,residualError);
            }

            std::map<int,boost::shared_ptr<Straw> >::iterator straw=_straws.find(sid);
            if(straw!=_straws.end() && !std::isnan(time))
            {
              double previousStartTime=straw->second->getStartTime();
              if(std::isnan(previousStartTime))
              {
                findBoundaryT(_hitsTimeMinmax, hitT0);  //is it Ok to exclude all following hits from the time window?
                straw->second->setStartTime(hitT0);
                straw->second->getComponentInfo()->setText(1,Form("hitT0(s): %gns",hitT0/CLHEP::ns));
                straw->second->getComponentInfo()->setText(2,Form("hit time(s): %gns",time/CLHEP::ns));
                straw->second->getComponentInfo()->setText(3,Form("strawhit time(s): %gns",strawtime/CLHEP::ns));
                residualGraph->GetXaxis()->SetTitle("z [mm]");
                residualGraph->GetYaxis()->SetTitle("Residual [??]");
                straw->second->getComponentInfo()->getHistVector().push_back(boost::dynamic_pointer_cast<TObject>(residualGraph));
                _hits.push_back(straw->second);
              }
              else
              {
                straw->second->getComponentInfo()->expandLine(1,Form("%gns",hitT0/CLHEP::ns));
                straw->second->getComponentInfo()->expandLine(2,Form("%gns",time/CLHEP::ns));
                straw->second->getComponentInfo()->expandLine(3,Form("%gns",strawtime/CLHEP::ns));
              }

              const boost::shared_ptr<std::string> strawname=straw->second->getComponentInfo()->getName();
              boost::shared_ptr<ComponentInfo> info(new ComponentInfo());
              info->setName(Form("Drift Radius for %s",strawname->c_str()));
              info->setText(0,strawname->c_str());
              info->setText(1,Form("Drift Radius %gcm",driftRadius/CLHEP::cm));
              boost::shared_ptr<Cylinder> driftradius(new Cylinder(p.x(),p.y(),p.z(),
                                                          phi+TMath::Pi()/2.0,theta,0,
                                                          5, //the halflength of 5 has no meaning
                                                          0,driftRadius,hitT0,
                                                          _geometrymanager, _topvolume, _mainframe, info, false));
              _components.push_back(driftradius);
              _driftradii.push_back(driftradius);
            }
          }
        }
      }
    }
  }

  // KalSeed hits
  const mu2e::KalSeedCollection *kalSeedsWithHits=contentSelector->getSelectedHitCollection<mu2e::KalSeedCollection>();
  if(kalSeedsWithHits!=NULL)
  {
    for(size_t i=0; i<kalSeedsWithHits->size(); i++)
    {
      const mu2e::KalSeed &kalseed = kalSeedsWithHits->at(i);
      const std::vector<mu2e::TrkStrawHitSeed> &hits = kalseed.hits();
      for(size_t j=0; j<hits.size(); j++)
      {
        const mu2e::TrkStrawHitSeed &hit = hits.at(j);
        int    sid = hit.strawId().asUint16();
        double time = hit.hitTime();

        std::map<int,boost::shared_ptr<Straw> >::iterator straw=_straws.find(sid);
        if(straw!=_straws.end() && !std::isnan(time))
        {
          double previousStartTime=straw->second->getStartTime();
          if(std::isnan(previousStartTime))
          {
            findBoundaryT(_hitsTimeMinmax, time);  //is it Ok to exclude all following hits from the time window?
            straw->second->setStartTime(time);
            straw->second->getComponentInfo()->setText(1,Form("hit time(s): %gns",time/CLHEP::ns));
            _hits.push_back(straw->second);
          }
          else
          {
            straw->second->getComponentInfo()->expandLine(1,Form("%gns",time/CLHEP::ns));
          }
        }
      }
    }
  }

  // StepPoints at calorimeter
  const mu2e::StepPointMCCollection *calosteppoints=contentSelector->getSelectedCaloHitCollection<mu2e::StepPointMCCollection>();
  if(calosteppoints!=nullptr)
  {
    _numberCrystalHits=calosteppoints->size();
    std::vector<mu2e::StepPointMC>::const_iterator iter;
    for(iter=calosteppoints->begin(); iter!=calosteppoints->end(); iter++)
    {
      const mu2e::StepPointMC& calohit = *iter;
      int crystalid = calohit.volumeId();
      int trackid = calohit.trackId().asInt();
      double time = timeOffsets.timeWithOffsetsApplied(calohit);
      double energy = calohit.eDep();
      std::map<int,boost::shared_ptr<VirtualShape> >::iterator crystal=_crystals.find(crystalid);
      if(crystal!=_crystals.end() && !std::isnan(time))
      {
        double previousStartTime=crystal->second->getStartTime();
        if(std::isnan(previousStartTime))
        {
          findBoundaryT(_hitsTimeMinmax, time);  //is it Ok to exclude all following hits from the time window?
          crystal->second->setStartTime(time);
          crystal->second->getComponentInfo()->setText(2,Form("hit time(s): %gns",time/CLHEP::ns));
          crystal->second->getComponentInfo()->setText(3,Form("deposited energy(s): %geV",energy/CLHEP::eV));
          crystal->second->getComponentInfo()->setText(4,Form("track ID(s): %i",trackid));
          _crystalhits.push_back(crystal->second);
        }
        else
        {
          crystal->second->getComponentInfo()->expandLine(2,Form("%gns",time/CLHEP::ns));
          crystal->second->getComponentInfo()->expandLine(3,Form("%geV",energy/CLHEP::eV));
          crystal->second->getComponentInfo()->expandLine(4,Form("%i",trackid));
        }
      }
    }
  }

  const mu2e::CaloHitCollection *calohits=contentSelector->getSelectedCaloHitCollection<mu2e::CaloHitCollection>();
  art::ServiceHandle<mu2e::GeometryService> geoservice;
  if(calohits!=nullptr && (geoservice->hasElement<mu2e::DiskCalorimeter>()))
  {
    _numberCrystalHits=calohits->size();  //this is not accurate since the return value gives the RO hits
    std::vector<mu2e::CaloHit>::const_iterator iter;
    for(iter=calohits->begin(); iter!=calohits->end(); iter++)
    {
      const mu2e::CaloHit& calohit = *iter;
      int roid = calohit.crystalID();
      int crystalid=0;

      if(geoservice->hasElement<mu2e::DiskCalorimeter>())
      {
        mu2e::GeomHandle<mu2e::DiskCalorimeter> diskCalo;
        crystalid=diskCalo->caloIDMapper().crystalIDFromSiPMID(roid);
      }
      double time = calohit.time();
      double energy = calohit.energyDep();
      std::map<int,boost::shared_ptr<VirtualShape> >::iterator crystal=_crystals.find(crystalid);
      if(crystal!=_crystals.end() && !std::isnan(time))
      {
        double previousStartTime=crystal->second->getStartTime();
        if(std::isnan(previousStartTime))
        {
          findBoundaryT(_hitsTimeMinmax, time);  //is it Ok to exclude all following hits from the time window?
          crystal->second->setStartTime(time);
          crystal->second->getComponentInfo()->setText(2,Form("hit time(s): %gns",time/CLHEP::ns));
          crystal->second->getComponentInfo()->setText(3,Form("deposited energy(s): %geV",energy/CLHEP::eV));
          crystal->second->getComponentInfo()->setText(4,Form("RO ID(s): %i",roid));
          _crystalhits.push_back(crystal->second);
        }
        else
        {
          crystal->second->getComponentInfo()->expandLine(2,Form("%gns",time/CLHEP::ns));
          crystal->second->getComponentInfo()->expandLine(3,Form("%geV",energy/CLHEP::eV));
          crystal->second->getComponentInfo()->expandLine(4,Form("%i",roid));
        }
      }
    }
  }

//CRV waveforms
  for(std::map<int,boost::shared_ptr<Cube> >::iterator crvbar=_crvscintillatorbars.begin(); crvbar!=_crvscintillatorbars.end(); crvbar++)
  {
    crvbar->second->getComponentInfo()->getHistVector().clear();
  }

  mu2e::ConditionsHandle<mu2e::CrvParams> crvPar("ignored");
  double digitizationPeriod = crvPar->digitizationPeriod;
  double recoPulsePedestal  = crvPar->pedestal;

  double TDC0time = contentSelector->getTDC0time();
  const std::vector<art::Handle<mu2e::CrvDigiCollection> > &crvDigisVector = contentSelector->getSelectedCrvDigiCollection();
  for(size_t i=0; i<crvDigisVector.size(); i++)
  {
    const art::Handle<mu2e::CrvDigiCollection> &crvDigis = crvDigisVector[i];
    std::string moduleLabel = crvDigis.provenance()->moduleLabel();
    for(size_t j=0; j<crvDigis->size(); j++)
    {
      mu2e::CrvDigi const& digi(crvDigis->at(j));
      int index = digi.GetScintillatorBarIndex().asInt();
      int sipm  = digi.GetSiPMNumber();
      std::string multigraphName = Form("Waveform (%s) SiPM %i",moduleLabel.c_str(),sipm);
      std::map<int,boost::shared_ptr<Cube> >::iterator crvbar=_crvscintillatorbars.find(index);
      if(crvbar!=_crvscintillatorbars.end())
      {
        //each digi collection and each SiPM gets its own multigraph
        bool newMultigraph=true;
        int multigraphIndex=0;
        std::vector<boost::shared_ptr<TObject> > &v=crvbar->second->getComponentInfo()->getHistVector();
        for(size_t k=0; k<v.size(); k++)
        {
          //check whether multigraph exists already for this module label and SiPM
          if(multigraphName.compare(v[k]->GetName())==0) {newMultigraph=false; multigraphIndex=k; break;}
        }
        if(newMultigraph)
        {
          boost::shared_ptr<TMultiGraph> waveform(new TMultiGraph(multigraphName.c_str(),multigraphName.c_str()));
          v.push_back(boost::dynamic_pointer_cast<TObject>(waveform));
          multigraphIndex=v.size()-1;
        }

        TGraph *graph = new TGraph(mu2e::CrvDigi::NSamples);
        graph->SetMarkerStyle(20);
        graph->SetMarkerSize(2);
        for(size_t k=0; k<mu2e::CrvDigi::NSamples; k++)
        {
          graph->SetPoint(k,TDC0time+(digi.GetStartTDC()+k)*digitizationPeriod,digi.GetADCs()[k]);
        }
        boost::dynamic_pointer_cast<TMultiGraph>(v[multigraphIndex])->Add(graph,"p");
      }
    }
  }

//CRV reco pulses
  const mu2e::CrvRecoPulseCollection *crvRecoPulses=contentSelector->getSelectedCrvHitCollection<mu2e::CrvRecoPulseCollection>();
  if(crvRecoPulses!=nullptr)
  {
    for(size_t i=0; i<crvRecoPulses->size(); i++)
    {
      const mu2e::CrvRecoPulse &recoPulse = crvRecoPulses->at(i);
      int    index = recoPulse.GetScintillatorBarIndex().asInt();
      int    sipm  = recoPulse.GetSiPMNumber();
      double time  = recoPulse.GetPulseTime();
      int    PEs   = recoPulse.GetPEs();

      std::map<int,boost::shared_ptr<Cube> >::iterator crvbar=_crvscintillatorbars.find(index);
      if(crvbar!=_crvscintillatorbars.end() && !std::isnan(time))
      {
        double previousStartTime=crvbar->second->getStartTime();
        if(std::isnan(previousStartTime)) _crvhits.push_back(crvbar->second);  //first reco hit of this counter

        if(std::isnan(previousStartTime) || time<previousStartTime) crvbar->second->setStartTime(time);

        if(std::isnan(previousStartTime))
        {
          findBoundaryT(_hitsTimeMinmax, time);  //is it Ok to exclude all following hits from the time window?
          crvbar->second->getComponentInfo()->setText(3,"Reco pulse SiPM0 PEs/time: ");
          crvbar->second->getComponentInfo()->setText(4,"Reco pulse SiPM1 PEs/time: ");
          crvbar->second->getComponentInfo()->setText(5,"Reco pulse SiPM2 PEs/time: ");
          crvbar->second->getComponentInfo()->setText(6,"Reco pulse SiPM3 PEs/time: ");
        }
        crvbar->second->getComponentInfo()->expandLine(sipm+3,Form("%iPEs/%.1fns",PEs,time/CLHEP::ns));

        //each digi collection and each SiPM gets its own multigraph
        std::vector<boost::shared_ptr<TObject> > &v=crvbar->second->getComponentInfo()->getHistVector();
        for(size_t k=0; k<v.size(); k++)
        {
          //check whether this multigraph is for the current SiPM
          const char *multigraphName = v[k]->GetName();
          int nameLength = strlen(multigraphName);
          if(nameLength<1) continue;
          if(atoi(multigraphName+nameLength-1)==sipm)
          {
            double fitParam0 = recoPulse.GetPulseHeight()*TMath::E();
            double fitParam1 = recoPulse.GetPulseTime();
            double fitParam2 = recoPulse.GetPulseBeta();

            TList *functionList = boost::dynamic_pointer_cast<TMultiGraph>(v[k])->GetListOfFunctions();
            if(functionList->GetSize()==0) functionList->Add(new TF1("pedestal",Form("%f",recoPulsePedestal))); //TODO: Use compiled function
            TF1 *f = new TF1(Form("peakfitter%i",functionList->GetSize()),
                             Form("%f*(TMath::Exp(-(x-%f)/%f-TMath::Exp(-(x-%f)/%f)))",
                             fitParam0,fitParam1,fitParam2,fitParam1,fitParam2));  //TODO: Use compiled function
            f->SetLineWidth(2);
            f->SetLineColor(2);
            if(recoPulse.GetRecoPulseFlags().test(mu2e::CrvRecoPulseFlagEnums::failedFit)) f->SetLineStyle(2);
            functionList->Add(f);
          }
        }
      }
    }
  }

  const mu2e::PhysicalVolumeInfoMultiCollection *physicalVolumesMulti=contentSelector->getPhysicalVolumeInfoMultiCollection();

  resetBoundaryP(_tracksMinmax);
  std::vector<ContentSelector::trackInfoStruct> trackInfos;
  std::vector<const mu2e::SimParticleCollection*> simParticleCollectionVector=contentSelector->getSelectedTrackCollection<mu2e::SimParticleCollection>(trackInfos);
  for(unsigned int i=0; i<simParticleCollectionVector.size(); i++)
  {
    const mu2e::SimParticleCollection *simParticles=simParticleCollectionVector[i];
    cet::map_vector<mu2e::SimParticle>::const_iterator iter;
    for(iter=simParticles->begin(); iter!=simParticles->end(); iter++)
    {
      const mu2e::SimParticle& particle = iter->second;
      art::Ptr<mu2e::SimParticle> particlePtr(trackInfos[i].productId, &particle, iter->first.asUint());
      double timeOffset = timeOffsets.totalTimeOffset(particlePtr);
      int id = particle.id().asInt();   //is identical with cet::map_vector_key& particleKey = iter->first;
      int parentid = particle.parentId().asInt();
      int particleid=particle.pdgId();
      int trackclass=trackInfos[i].classID;
      int trackclassindex=trackInfos[i].index;
      std::string particlecollection=trackInfos[i].entryText;
      std::string particlename=HepPID::particleName(particle.pdgId());
      std::string startVolumeName="unknown volume";
      std::string endVolumeName="unknown volume";
      if(physicalVolumesMulti!=nullptr)
      {
        mu2e::PhysicalVolumeMultiHelper volumeMultiHelper(*physicalVolumesMulti);
        startVolumeName=volumeMultiHelper.startVolume(particle).name();
        endVolumeName=volumeMultiHelper.endVolume(particle).name();
      }
      double x1=particle.startPosition().x() - _detSysOrigin.x();
      double y1=particle.startPosition().y() - _detSysOrigin.y();
      double z1=particle.startPosition().z() - _detSysOrigin.z();
      double t1=particle.startGlobalTime()+timeOffset;
      double e1=particle.startMomentum().e();
      double x2=particle.endPosition().x() - _detSysOrigin.x();
      double y2=particle.endPosition().y() - _detSysOrigin.y();
      double z2=particle.endPosition().z() - _detSysOrigin.z();
      double t2=particle.endGlobalTime()+timeOffset;
      double e2=particle.endMomentum().e();
      findBoundaryT(_tracksTimeMinmax, t1);
      findBoundaryT(_tracksTimeMinmax, t2);
      findBoundaryP(_tracksMinmax, x1, y1, z1);
      findBoundaryP(_tracksMinmax, x2, y2, z2);

      boost::shared_ptr<ComponentInfo> info(new ComponentInfo());
      info->setName(Form("Track %i  %s  (%s)",id,particlename.c_str(),particlecollection.c_str()));
      if(parentid!=0) info->setText(0,Form("Track %i  %s  Parent %i  (%s)",id,particlename.c_str(),parentid,particlecollection.c_str()));
      else info->setText(0,Form("Track %i  %s  generated track  (%s)",id,particlename.c_str(),particlecollection.c_str()));
      info->setText(1,Form("Start Energy %gMeV  End Energy %gMeV",e1/CLHEP::MeV,e2/CLHEP::MeV));
      info->setText(2,Form("Created by %s in %s",particle.creationCode().name().c_str(),startVolumeName.c_str()));
      info->setText(3,Form("Destroyed by %s in %s",particle.stoppingCode().name().c_str(),endVolumeName.c_str()));
      info->setText(4,"Daughter IDs:");
      std::vector<art::Ptr<mu2e::SimParticle> >::const_iterator daughter;
      for(daughter=particle.daughters().begin();
          daughter!=particle.daughters().end();
          daughter++)
      {
        info->expandLine(4,Form("%lu",(*daughter)->id().asInt()));
      }
      boost::shared_ptr<Track> shape(new Track(x1,y1,z1,t1, x2,y2,z2,t2,
                                               particleid, trackclass, trackclassindex, e1,
                                               _geometrymanager, _topvolume, _mainframe, info, false));
      findTrajectory(contentSelector,shape,particle.id(), timeOffset, trackInfos[i]);
      _components.push_back(shape);
      _tracks.push_back(shape);
    }
  }

  trackInfos.clear();
  std::vector<const mu2e::KalRepCollection*> kalRepCollectionVector=contentSelector->getSelectedTrackCollection<mu2e::KalRepCollection>(trackInfos);
  for(unsigned int i=0; i<kalRepCollectionVector.size(); i++)
  {
    const mu2e::KalRepCollection *kalReps=kalRepCollectionVector[i];
    for(unsigned int j=0; j<kalReps->size(); j++)
    {
      KalRep const* kalrep = kalReps->get(j);
        int trackclass=trackInfos[i].classID;
        int trackclassindex=trackInfos[i].index;
        std::string trackcollection=trackInfos[i].entryText;
        int particleid=kalrep->particleType().particleType();
        std::string particlename=HepPID::particleName(particleid);
        std::string c=Form("Kalman Track %i  %s  (%s)",j,particlename.c_str(),trackcollection.c_str());
        boost::shared_ptr<ComponentInfo> info(new ComponentInfo());
        info->setName(c.c_str());
        info->setText(0,c.c_str());

        double hitcount=0;
        double offset=0;
        TrkHitVector const& hots=kalrep->hitVector();
        if(hots.size()>0)
        {
          boost::shared_ptr<TGraphErrors> residualGraph(new TGraphErrors());
          residualGraph->SetTitle("Residual Graph");
          for(auto iter=hots.begin(); iter!=hots.end(); iter++)
          {
            const TrkHit *hitOnTrack = *iter;
            const mu2e::TrkStrawHit* strawHit = dynamic_cast<const mu2e::TrkStrawHit*>(hitOnTrack);
            if(strawHit)
            {
              double strawTime   = strawHit->hitT0()._t0/CLHEP::ns;
              double trackTime   = strawTime*CLHEP::ns;  //TODO: add correction for drift time
              double weight= strawHit->weight();
              double fltLen= strawHit->fltLen();
              const HepPoint &p=strawHit->hitTraj()->position(strawHit->hitLen());
              double t     = kalrep->arrivalTime(fltLen);
              offset+=(trackTime-t)*weight;
              hitcount+=weight;

              double residual, residualError;
              if(strawHit->resid(residual, residualError))
              {
                int n=residualGraph->GetN();
                residualGraph->SetPoint(n,p.z(),residual);
                residualGraph->SetPointError(n,0,residualError);
              }
            }
          }
          residualGraph->GetXaxis()->SetTitle("z [mm]");
          residualGraph->GetYaxis()->SetTitle("Residual [??]");
          info->getHistVector().push_back(boost::dynamic_pointer_cast<TObject>(residualGraph));
        }
        if(hitcount>0) offset/=hitcount; else offset=0;

        double fltLMin=kalrep->startValidRange();
        double fltLMax=kalrep->endValidRange();
        double p1=kalrep->momentum(fltLMin).mag();
        double p2=kalrep->momentum(fltLMax).mag();
        double x1=kalrep->position(fltLMin).x();
        double y1=kalrep->position(fltLMin).y();
        double z1=kalrep->position(fltLMin).z();
        double x2=kalrep->position(fltLMax).x();
        double y2=kalrep->position(fltLMax).y();
        double z2=kalrep->position(fltLMax).z();
        double t1=kalrep->arrivalTime(fltLMin)+offset;
        double t2=kalrep->arrivalTime(fltLMax)+offset;
        boost::shared_ptr<Track> track(new Track(x1,y1,z1,t1, x2,y2,z2,t2,
                                                 particleid, trackclass, trackclassindex, p1,
                                                 _geometrymanager, _topvolume, _mainframe, info, false));
        _components.push_back(track);
        _tracks.push_back(track);

        double fltStep = (fltLMax - fltLMin)/400.0;
        for(unsigned int step = 0; step <= 400.0; step++)
        {
          double fltL = fltLMin + step*fltStep;
          double   t = kalrep->arrivalTime(fltL)+offset;
          HepPoint p = kalrep->position(fltL);
          findBoundaryT(_tracksTimeMinmax, t);
          findBoundaryP(_tracksMinmax, p.x(), p.y(), p.z());
          track->addTrajectoryPoint(p.x(), p.y(), p.z(), t);
        }

	int charge = kalrep->charge();
        double t0=kalrep->t0().t0();
        double firsthitfltlen = kalrep->lowFitRange();
        double lasthitfltlen = kalrep->hiFitRange();
        double entlen = min(firsthitfltlen,lasthitfltlen);
        double loclen(0.0);
        const TrkSimpTraj* ltraj = kalrep->localTrajectory(entlen,loclen);
        const CLHEP::HepVector &params=ltraj->parameters()->parameter();
        double d0 = params[0];
        double om = params[2];
        double rmax = d0+2.0/om;

	info->setText(1,Form("Charge %i",charge));
	info->setText(2,Form("Start Momentum %gMeV/c  End Momentum %gMeV/c",p1/CLHEP::MeV,p2/CLHEP::MeV));
	info->setText(3,Form("t0 %gns  d0 %gmm  rmax %gmm",t0/CLHEP::ns,d0/CLHEP::mm,rmax/CLHEP::mm));
    }
  }

  // KalSeed tracks
  trackInfos.clear();
  std::vector<const mu2e::KalSeedCollection*> kalSeedCollectionVector=contentSelector->getSelectedTrackCollection<mu2e::KalSeedCollection>(trackInfos);
  for(size_t i=0; i<kalSeedCollectionVector.size(); i++)
  {
    const mu2e::KalSeedCollection *kalSeeds=kalSeedCollectionVector[i];
    for(size_t j=0; j<kalSeeds->size(); j++)
    {
      const mu2e::KalSeed &kalseed = kalSeeds->at(j);
      int trackclass=trackInfos[i].classID;
      int trackclassindex=trackInfos[i].index;
      std::string trackcollection=trackInfos[i].entryText;
      int particleid=kalseed.particle();
      std::string particlename=HepPID::particleName(particleid);

      const std::vector<mu2e::KalSegment> &segments = kalseed.segments();
      size_t nSegments=segments.size();
      if(nSegments==0) continue;
      const mu2e::KalSegment &segmentFirst = kalseed.segments().front();
      const mu2e::KalSegment &segmentLast = kalseed.segments().back();
      double fltLMin=segmentFirst.fmin();
      double fltLMax=segmentLast.fmax();
      XYZVec momvec1, momvec2;
      segmentFirst.mom(fltLMin, momvec1);
      segmentLast.mom(fltLMax, momvec2);
      double p1=Geom::Hep3Vec(momvec1).mag();
      double p2=Geom::Hep3Vec(momvec2).mag();

      double t0   = kalseed.t0().t0();
      double flt0 = kalseed.flt0();
      double mass = ptable->particle(kalseed.particle()).ref().mass().value();
      double v  = mu2e::TrkUtilities::beta(mass,(p1+p2)/2.0)*CLHEP::c_light;

      boost::shared_ptr<ComponentInfo> info(new ComponentInfo());
      std::string c=Form("KalSeed Track %lu  %s  (%s)",j,particlename.c_str(),trackcollection.c_str());
      info->setName(c.c_str());
      info->setText(0,c.c_str());
      info->setText(1,Form("Start Momentum %gMeV/c  End Momentum %gMeV/c",p1/CLHEP::MeV,p2/CLHEP::MeV));

      for(size_t k=0; k<nSegments; k++)
      {
        const mu2e::KalSegment &segment = segments.at(k);

        fltLMin=segment.fmin();
        fltLMax=segment.fmax();
/*
//interpolation between segments doesn't seem to work anymore
        if(k>0)
        {
          double fltLMaxPrev=segments.at(k-1).fmax();
          fltLMin=(fltLMin+fltLMaxPrev)/2.0;
        }
        if(k+1<nSegments)
        {
          double fltLMinNext=segments.at(k+1).fmin();
          fltLMax=(fltLMax+fltLMinNext)/2.0;
        }
*/

        XYZVec pos1, pos2;
        segment.helix().position(fltLMin,pos1);
        segment.helix().position(fltLMax,pos2);
        double x1=Geom::Hep3Vec(pos1).x();
        double y1=Geom::Hep3Vec(pos1).y();
        double z1=Geom::Hep3Vec(pos1).z();
        double x2=Geom::Hep3Vec(pos2).x();
        double y2=Geom::Hep3Vec(pos2).y();
        double z2=Geom::Hep3Vec(pos2).z();
        double t1=t0+(fltLMin-flt0)/v;
        double t2=t0+(fltLMax-flt0)/v;
        boost::shared_ptr<Track> track(new Track(x1,y1,z1,t1, x2,y2,z2,t2,
                                                 particleid, trackclass, trackclassindex, p1,
                                                 _geometrymanager, _topvolume, _mainframe, info, false));
        _components.push_back(track);
        _tracks.push_back(track);

        for(double fltL=fltLMin; fltL<=fltLMax; fltL+=1.0)
        {
          double t=t0+(fltL-flt0)/v;
          XYZVec pos;
          segment.helix().position(fltL,pos);
          CLHEP::Hep3Vector p = Geom::Hep3Vec(pos);
          findBoundaryT(_tracksTimeMinmax, t);
          findBoundaryP(_tracksMinmax, p.x(), p.y(), p.z());
          track->addTrajectoryPoint(p.x(), p.y(), p.z(), t);
        }
      }
    }
  }

  // TrkExt track
  trackInfos.clear();
  std::vector<const mu2e::TrkExtTrajCollection*> trkExtTrajCollectionVector=contentSelector->getSelectedTrackCollection<mu2e::TrkExtTrajCollection>(trackInfos);
  for(unsigned int i=0; i<trkExtTrajCollectionVector.size(); i++)
  {
    // Read a TrkExtTrajCollection
    const mu2e::TrkExtTrajCollection & trkExtTrajCollection = *trkExtTrajCollectionVector[i];
    for(unsigned int j=0; j<trkExtTrajCollection.size(); j++)
    {
      // read a TrkExtTraj
      const mu2e::TrkExtTraj &trkExtTraj = trkExtTrajCollection.at(j);
      int particleid=11;
      int trackclass=trackInfos[i].classID;
      int trackclassindex=trackInfos[i].index;
      std::string trackcollection=trackInfos[i].entryText;

      std::string particlename=HepPID::particleName(particleid);
      boost::shared_ptr<ComponentInfo> info(new ComponentInfo());
      std::string c=Form("TrkExt Trajectory %i  %s  (%s)", trkExtTraj.id(), particlename.c_str(),trackcollection.c_str());
      info->setName(c.c_str());
      info->setText(0,c.c_str());

      double p1 = trkExtTraj.front().momentum().mag();
      double x1 = trkExtTraj.front().x();
      double y1 = trkExtTraj.front().y();
      double z1 = trkExtTraj.front().z();
      double x2 = trkExtTraj.back().x();
      double y2 = trkExtTraj.back().y();
      double z2 = trkExtTraj.back().z();
      double t1 = 0;
      double t2 = 0;
      boost::shared_ptr<Track> track(new Track(x1,y1,z1,t1, x2,y2,z2,t2,
                                               particleid, trackclass, trackclassindex, p1,
                                               _geometrymanager, _topvolume, _mainframe, info, false));
      _components.push_back(track);
      _tracks.push_back(track);

      for (unsigned int k = 0 ; k < trkExtTraj.size() ; k+=10) {
        const mu2e::TrkExtTrajPoint & trkExtTrajPoint = trkExtTraj[k];
        track->addTrajectoryPoint(trkExtTrajPoint.x(), trkExtTrajPoint.y(), trkExtTrajPoint.z(), 0);
      }
    }
  }
}

void DataInterface::findTrajectory(boost::shared_ptr<ContentSelector> const &contentSelector,
                                   boost::shared_ptr<Track> const &track, const cet::map_vector_key &id,
                                   double timeOffset,
                                   const ContentSelector::trackInfoStruct &trackInfo)
{
  const mu2e::MCTrajectoryCollection *mcTrajectories=contentSelector->getMCTrajectoryCollection(trackInfo);
  if(mcTrajectories!=nullptr)
  {
    std::map<art::Ptr<mu2e::SimParticle>,mu2e::MCTrajectory>::const_iterator traj_iter;
    for(traj_iter=mcTrajectories->begin(); traj_iter!=mcTrajectories->end(); traj_iter++)
    {
      if(traj_iter->first->id()==id)
//      if(traj_iter->second.sim()->id()==id)
      {
        const auto& points = traj_iter->second.points();
        for(auto point_iter=points.begin(); point_iter!=points.end(); ++point_iter)
        {
          track->addTrajectoryPoint(point_iter->x()-_detSysOrigin.x(),
                                    point_iter->y()-_detSysOrigin.y(),
                                    point_iter->z()-_detSysOrigin.z(),
                                    point_iter->t()+timeOffset);
        }
      }
    }
    return;
  }
}

void DataInterface::removeNonGeometryComponents()
{
  std::list<boost::shared_ptr<VirtualShape> >::iterator iter=_components.begin();
  while(iter!=_components.end())
  {
    if(!(*iter)->isGeometry()) {iter=_components.erase(iter);} //things like tracks and drift radii
    else iter++;
  }

  std::vector<boost::shared_ptr<Straw> >::const_iterator hit;
  for(hit=_hits.begin(); hit!=_hits.end(); hit++)
  {
    for(int i=1; i<7; i++) (*hit)->getComponentInfo()->removeLine(i);  //keep first line
    (*hit)->getComponentInfo()->getHistVector().clear();
    (*hit)->setHitNumber(-1);
    (*hit)->setStartTime(NAN);
    (*hit)->start();
  }
  std::vector<boost::shared_ptr<VirtualShape> >::const_iterator crystalhit;
  for(crystalhit=_crystalhits.begin(); crystalhit!=_crystalhits.end(); crystalhit++)
  {
    for(int i=1; i<7; i++) (*crystalhit)->getComponentInfo()->removeLine(i); //keep first line
    (*crystalhit)->getComponentInfo()->getHistVector().clear();
    (*crystalhit)->setStartTime(NAN);
    (*crystalhit)->start();
  }
  std::vector<boost::shared_ptr<Cube> >::const_iterator crvhit;
  for(crvhit=_crvhits.begin(); crvhit!=_crvhits.end(); crvhit++)
  {
    for(int i=1; i<7; i++) (*crvhit)->getComponentInfo()->removeLine(i); //keep first line
    (*crvhit)->getComponentInfo()->getHistVector().clear();
    (*crvhit)->setStartTime(NAN);
    (*crvhit)->start();
  }

  _hits.clear();
  _crystalhits.clear();
  _crvhits.clear();
  _tracks.clear();  //will call the d'tors of all tracks, since they aren't used anywhere anymore
  _driftradii.clear(); //will call the d'tors of all driftradii, since they aren't used anywhere anymore

  _mainframe->getHistDrawVector().clear();
}

void DataInterface::removeAllComponents()
{
  _components.clear();
  _straws.clear();
  _crystals.clear();
  _crvscintillatorbars.clear();
  _hits.clear();
  _crystalhits.clear();
  _crvhits.clear();
  _tracks.clear();
  _driftradii.clear();
  _supportstructures.clear();
  _otherstructures.clear();
  _crvscintillatorbars.clear();
  _mbsstructures.clear();
  _mecostylepastructures.clear();
  delete _geometrymanager;
  _geometrymanager=nullptr;

  _mainframe->getHistDrawVector().clear();
}

}