/* ======================================================================== *\ ! ! * ! * This file is part of MARS, the MAGIC Analysis and Reconstruction ! * Software. It is distributed to you in the hope that it can be a useful ! * and timesaving tool in analysing Data of imaging Cerenkov telescopes. ! * It is distributed WITHOUT ANY WARRANTY. ! * ! * Permission to use, copy, modify and distribute this software and its ! * documentation for any purpose is hereby granted without fee, ! * provided that the above copyright notice appear in all copies and ! * that both that copyright notice and this permission notice appear ! * in supporting documentation. It is provided "as is" without express ! * or implied warranty. ! * ! ! ! Author(s): Thomas Bretz, 05/2002 ! Author(s): Harald Kornmayer, 1/2001 ! ! Copyright: MAGIC Software Development, 2000-2003 ! ! \* ======================================================================== */ ///////////////////////////////////////////////////////////////////////////// // // MHCamera // // Camera Display, based on a TH1D. Pleas be carefull using the // underlaying TH1D. // // To change the scale to a logarithmic scale SetLogy() of the Pad. // //////////////////////////////////////////////////////////////////////////// #include "MHCamera.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "MLog.h" #include "MLogManip.h" #include "MH.h" #include "MHexagon.h" #include "MGeomPix.h" #include "MGeomCam.h" #include "MCerPhotPix.h" #include "MCerPhotEvt.h" #include "MPedestalPix.h" #include "MPedestalCam.h" #include "MCurrents.h" #include "MCamEvent.h" #include "MImgCleanStd.h" #define kItemsLegend 48 // see SetPalette(1,0) ClassImp(MHCamera); using namespace std; // ------------------------------------------------------------------------ // // Default Constructor. To be used by the root system ONLY. // MHCamera::MHCamera() : TH1D(), fGeomCam(NULL), fColors(kItemsLegend), fOptStat(-1) { SetDirectory(NULL); fNotify = NULL; #if ROOT_VERSION_CODE < ROOT_VERSION(3,01,06) SetPalette(1, 0); #else SetPalette(51, 0); #endif } // ------------------------------------------------------------------------ // // Constructor. Makes a clone of MGeomCam. Removed the TH1D from the // current directory. Calls Sumw2(). Set the histogram line color // (for error bars) to Green and the marker style to kFullDotMedium. // MHCamera::MHCamera(const MGeomCam &geom, const char *name, const char *title) : TH1D(name, title, geom.GetNumPixels(), -0.5, geom.GetNumPixels()-0.5), fUsed(geom.GetNumPixels()), fColors(kItemsLegend) { fGeomCam = (MGeomCam*)geom.Clone(); SetDirectory(NULL); Sumw2(); SetLineColor(kGreen); SetMarkerStyle(kFullDotMedium); SetXTitle("Pixel Index"); fNotify = new TList; // // create the hexagons of the display // // root 3.02 // * base/inc/TObject.h: // register BIT(8) as kNoContextMenu. If an object has this bit set it will // not get an automatic context menu when clicked with the right mouse button. // // Construct all hexagons. Use new-operator with placement // for (Int_t i=0; i ROOT_VERSION(3,05,00) if (fBuffer) return BufferFill(x,1); #endif const Int_t bin = (Int_t)x+1; AddBinContent(bin); if (fSumw2.fN) fSumw2.fArray[bin]++; if (bin<=0 || bin>fNcells-2) return -1; fTsumw++; fTsumw2++; fTsumwx += x; fTsumwx2 += x*x; return bin; } // ------------------------------------------------------------------------ // // Taken from TH1D::Fill(). Uses the argument directly as bin index. // Doesn't increment the number of entries. // // -*-*-*-*-*-*Increment bin with abscissa X with a weight w*-*-*-*-*-*-*-* // ============================================= // // if x is less than the low-edge of the first bin, the Underflow bin is incremented // if x is greater than the upper edge of last bin, the Overflow bin is incremented // // If the storage of the sum of squares of weights has been triggered, // via the function Sumw2, then the sum of the squares of weights is incremented // by w^2 in the bin corresponding to x. // // -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-* Int_t MHCamera::Fill(Axis_t x, Stat_t w) { #if ROOT_VERSION_CODE > ROOT_VERSION(3,05,00) if (fBuffer) return BufferFill(x,w); #endif const Int_t bin = (Int_t)x+1; AddBinContent(bin, w); if (fSumw2.fN) fSumw2.fArray[bin] += w*w; if (bin<=0 || bin>fNcells-2) return -1; const Stat_t z = (w > 0 ? w : -w); fTsumw += z; fTsumw2 += z*z; fTsumwx += z*x; fTsumwx2 += z*x*x; return bin; } Stat_t MHCamera::GetMean(Int_t axis) const { Stat_t mean = 0; for (int i=1; i maximum) maximum = fArray[idx+1]; } else { for (Int_t idx=0; idx maximum) maximum = fArray[idx+1]; } return maximum; } // ------------------------------------------------------------------------ // // Call this function to draw the camera layout into your canvas. // Setup a drawing canvas. Add this object and all child objects // (hexagons, etc) to the current pad. If no pad exists a new one is // created. // // To draw a camera into its own pad do something like: // // TCanvas *c = new TCanvas; // c->Divide(2,1); // MGeomCamMagic m; // MHCamera *d=new MHCamera(&m); // d->FillRandom(); // c->cd(1); // gPad->SetBorderMode(0); // gPad->Divide(1,1); // gPad->cd(1); // d->Draw(); // d->SetBit(kCanDelete); // void MHCamera::Draw(Option_t *option) { // root 3.02: // gPad->SetFixedAspectRatio() Int_t col = 16; if (gPad) col = gPad->GetFillColor(); TVirtualPad *pad = gPad ? gPad : MH::MakeDefCanvas("CamDisplay", "Mars Camera Display", 656, 600); pad->SetBorderMode(0); pad->SetFillColor(col); AppendPad(option); } // ------------------------------------------------------------------------ // // Resizes the current pad so that the camera is displayed in its // correct aspect ratio // void MHCamera::SetRange() { const Float_t range = fGeomCam->GetMaxRadius()*1.05; // // Maintain aspect ratio // const float ratio = 1.15; // // Calculate width and height of the current pad in pixels // Float_t w = gPad->GetWw(); Float_t h = gPad->GetWh()*ratio; // // This prevents the pad from resizing itself wrongly // if (gPad->GetMother() != gPad) { w *= gPad->GetMother()->GetAbsWNDC(); h *= gPad->GetMother()->GetAbsHNDC(); } // // Set Range (coordinate system) of pad // gPad->Range(-range, -range, (2*ratio-1)*range, range); // // Resize Pad to given ratio // if (hSetPad((1.-h/w)/2, 0, (h/w+1.)/2, 1); else gPad->SetPad(0, (1.-w/h)/2, 1, (w/h+1.)/2); } // ------------------------------------------------------------------------ // // Updates the pixel colors and paints the pixels // void MHCamera::Update(Bool_t islog, Bool_t isbox, Bool_t iscol) { Double_t min = GetMinimum(kFALSE); Double_t max = GetMaximum(kFALSE); if (min==FLT_MAX) { min = 0; max = 1; } if (min==max) max += 1; UpdateLegend(min, max, islog); MHexagon hex; for (Int_t i=0; i - Pixel Index #" << i << " contents is NaN (Not a Number)..." << endl; hex.SetFillColor(GetColor(fArray[i+1], min, max, islog)); } else hex.SetFillColor(10); MGeomPix &pix = (*fGeomCam)[i]; if (!isbox) hex.PaintHexagon(pix.GetX(), pix.GetY(), pix.GetD()); else if (IsUsed(i) && !TMath::IsNaN(fArray[i+1])) { Float_t size = pix.GetD()*(fArray[i+1]-min)/(max-min); if (size>pix.GetD()) size=pix.GetD(); hex.PaintHexagon(pix.GetX(), pix.GetY(), size); } } } // ------------------------------------------------------------------------ // // Print minimum and maximum // void MHCamera::Print(Option_t *) const { cout << "Minimum: " << GetMinimum(); if (fMinimum==-1111) cout << " "; cout << endl; cout << "Maximum: " << GetMaximum(); if (fMaximum==-1111) cout << " "; cout << endl; } // ------------------------------------------------------------------------ // // Paint the y-axis title // void MHCamera::PaintAxisTitle() { const Float_t range = fGeomCam->GetMaxRadius()*1.05; const Float_t w = (1 + 1.5/sqrt((float)(fNcells-2)))*range; TLatex *ptitle = new TLatex(w, -.90*range, GetYaxis()->GetTitle()); ptitle->SetTextSize(0.05); ptitle->SetTextAlign(21); // box with the histogram title ptitle->SetTextColor(gStyle->GetTitleTextColor()); #if ROOT_VERSION_CODE > ROOT_VERSION(3,05,01) ptitle->SetTextFont(gStyle->GetTitleFont("")); #endif ptitle->Paint(); } // ------------------------------------------------------------------------ // // Paints the camera. // void MHCamera::Paint(Option_t *o) { TString opt(o); opt.ToLower(); if (opt.Contains("hist")) { opt.ReplaceAll("hist", ""); Int_t mode = gStyle->GetOptStat(); TVirtualPad *save = gPad; gPad=NULL; gStyle->SetOptStat(fOptStat<0 ? mode : fOptStat); gPad=save; TH1D::Paint(o); gPad=NULL; gStyle->SetOptStat(mode); gPad=save; return; } // Maintain aspect ratio SetRange(); Bool_t isbox = opt.Contains("box"); Bool_t iscol = isbox ? !opt.Contains("nocol") : 1; THistPainter paint; if (!TestBit(TH1::kNoStats)) { paint.SetHistogram(this); paint.PaintStat(fOptStat<0?gStyle->GetOptStat():fOptStat, NULL); } // Update Contents of the pixels and paint legend Update(gPad->GetLogy(), isbox, iscol); // Paint primitives (pixels, color legend, photons, ...) paint.PaintTitle(); PaintAxisTitle(); } // ------------------------------------------------------------------------ // // With this function you can change the color palette. For more // information see TStyle::SetPalette. Only palettes with 50 colors // are allowed. // In addition you can use SetPalette(52, 0) to create an inverse // deep blue sea palette // void MHCamera::SetPalette(Int_t ncolors, Int_t *colors) { // // If not enough colors are specified skip this. // if (ncolors>1 && ncolors<50) { cout << "MHCamera::SetPalette: Only default palettes with 50 colors are allowed... ignored." << endl; return; } // // If ncolors==52 create a reversed deep blue sea palette // if (ncolors==52) { gStyle->SetPalette(51, NULL); TArrayI c(kItemsLegend); for (int i=0; iGetColorPalette(i); gStyle->SetPalette(kItemsLegend, c.GetArray()); } else gStyle->SetPalette(ncolors, colors); fColors.Set(kItemsLegend); for (int i=0; iGetColorPalette(i); } void MHCamera::SetPrettyPalette() { if (!TString(GetDrawOption()).Contains("hist", TString::kIgnoreCase)) SetPalette(1, 0); } void MHCamera::SetDeepBlueSeaPalette() { if (!TString(GetDrawOption()).Contains("hist", TString::kIgnoreCase)) SetPalette(51, 0); } void MHCamera::SetInvDeepBlueSeaPalette() { if (!TString(GetDrawOption()).Contains("hist", TString::kIgnoreCase)) SetPalette(52, 0); } void MHCamera::DrawPixelIndices() { // FIXME: Is this correct? for (int i=0; iSetTextSize(0.3*h.GetD()/fGeomCam->GetMaxRadius()/1.05); } } void MHCamera::DrawSectorIndices() { for (int i=0; iSetTextSize(0.3*h.GetD()/fGeomCam->GetMaxRadius()/1.05); } } // ------------------------------------------------------------------------ // // Call this function to add a MCamEvent on top of the present contents. // Only 'used' pixels are added. // void MHCamera::AddCamContent(const MCamEvent &event, Int_t type) { // FIXME: Security check missing! for (Int_t idx=0; idxGetSize()!=fNcells-2) return; for (Int_t idx=0; idx(event)[idx]); // FIXME: Slow! if (used && (*const_cast(used))[idx]) SetUsed(idx); } fEntries++; } // ------------------------------------------------------------------------ // // Call this function to add a MCamEvent on top of the present contents. // 1 is added to each pixel if the contents of MCamEvent>threshold // void MHCamera::CntCamContent(const MCamEvent &event, Double_t threshold, Int_t type) { // FIXME: Security check missing! for (Int_t idx=0; idxthreshold) Fill(idx); } fEntries++; } // ------------------------------------------------------------------------ // // Call this function to add a TArrayD on top of the present contents. // 1 is added to each pixel if the contents of MCamEvent>threshold // void MHCamera::CntCamContent(const TArrayD &event, Double_t threshold, Bool_t ispos) { if (event.GetSize()!=fNcells-2) return; for (Int_t idx=0; idx(event)[idx]>threshold) Fill(idx); if (!ispos || fArray[idx+1]>0) SetUsed(idx); } fEntries++; } // ------------------------------------------------------------------------ // // Fill the pixels with random contents. // void MHCamera::FillRandom() { Reset(); // FIXME: Security check missing! for (Int_t idx=0; idxUniform()*fGeomCam->GetPixRatio(idx)); SetUsed(idx); } fEntries=1; } // ------------------------------------------------------------------------ // // Fill the colors in respect to the cleaning levels // void MHCamera::FillLevels(const MCerPhotEvt &event, Float_t lvl1, Float_t lvl2) { SetCamContent(event, 2); for (Int_t i=0; ilvl1) fArray[i+1] = 0; else if (fArray[i+1]>lvl2) fArray[i+1] = 1; else fArray[i+1] = 2; } } // ------------------------------------------------------------------------ // // Fill the colors in respect to the cleaning levels // void MHCamera::FillLevels(const MCerPhotEvt &event, const MImgCleanStd &clean) { FillLevels(event, clean.GetCleanLvl1(), clean.GetCleanLvl2()); } // ------------------------------------------------------------------------ // // Reset the all pixel colors to a default value // void MHCamera::Reset(Option_t *opt) { TH1::Reset(opt); for (Int_t i=0; iSetPalette(1,0) // for the display. So we have to convert the value "wert" into // a color index that fits the color palette. // The range of the color palette is defined by the values fMinPhe // and fMaxRange. Between this values we have 50 color index, starting // with 0 up to 49. // Int_t MHCamera::GetColor(Float_t val, Float_t min, Float_t max, Bool_t islog) { if (TMath::IsNaN(val)) // FIXME: gLog! return 10; // // first treat the over- and under-flows // const Int_t maxcolidx = kItemsLegend-1; if (val >= max) return fColors[maxcolidx]; if (val <= min) return fColors[0]; // // calculate the color index // Float_t ratio; if (islog && min>0) ratio = log10(val/min) / log10(max/min); else ratio = (val-min) / (max-min); const Int_t colidx = (Int_t)(ratio*maxcolidx + .5); return fColors[colidx]; } TPaveStats *MHCamera::GetStats() { TObject *obj = 0; TIter Next(fFunctions); while ((obj = Next())) if (obj->InheritsFrom(TPaveStats::Class())) return static_cast(obj); return NULL; } // ------------------------------------------------------------------------ // // Change the text on the legend according to the range of the Display // void MHCamera::UpdateLegend(Float_t min, Float_t max, Bool_t islog) { TPaveStats *stats = GetStats(); const Float_t hndc = 0.92 - (stats ? stats->GetY1NDC() : 1); const Float_t range = fGeomCam->GetMaxRadius()*1.05; const Float_t H = (0.75-hndc)*range; const Float_t offset = hndc*range; const Float_t h = 2./kItemsLegend; const Float_t w = range/sqrt((float)(fNcells-2)); TBox newbox; TText newtxt; newtxt.SetTextSize(0.03); newtxt.SetTextAlign(12); #if ROOT_VERSION_CODE > ROOT_VERSION(3,01,06) newtxt.SetBit(/*kNoContextMenu|*/kCannotPick); newbox.SetBit(/*kNoContextMenu|*/kCannotPick); #endif const Float_t step = (islog && min>0 ? log10(max/min) : max-min) / kItemsLegend; const Int_t firsts = step*3 < 1e-8 ? 8 : (Int_t)floor(log10(step*3)); const TString opt = Form("%%.%if", firsts>0 ? 0 : abs(firsts)); for (Int_t i=0; i0) val = pow(10, step*i) * min; else val = min + step*i; //const bool dispexp = max-min>1.5 && fabs(val)>0.1 && fabs(val)<1e6; newtxt.PaintText(range+1.5*w, H*(i*h-1)-offset, Form(opt, val)); } for (Int_t i=0; iGetConvMm2Deg()*10))/10; text += "\\circ"; text = text.Strip(TString::kLeading); TLatex latex; latex.PaintLatex(-range*.85, -range*.75, 0, 0.04, text); } // ------------------------------------------------------------------------ // // Save primitive as a C++ statement(s) on output stream out // void MHCamera::SavePrimitive(ofstream &out, Option_t *opt) { cout << "MHCamera::SavePrimitive: Must be rewritten!" << endl; /* if (!gROOT->ClassSaved(TCanvas::Class())) fDrawingPad->SavePrimitive(out, opt); out << " " << fDrawingPad->GetName() << "->SetWindowSize("; out << fDrawingPad->GetWw() << "," << fDrawingPad->GetWh() << ");" << endl; */ } // ------------------------------------------------------------------------ // // compute the distance of a point (px,py) to the Camera // this functions needed for graphical primitives, that // means without this function you are not able to interact // with the graphical primitive with the mouse!!! // // All calcutations are done in pixel coordinates // Int_t MHCamera::DistancetoPrimitive(Int_t px, Int_t py) { if (TString(GetDrawOption()).Contains("hist", TString::kIgnoreCase)) return TH1D::DistancetoPrimitive(px, py); for (Int_t i=0; i0) continue; return i; } return -1; } // ------------------------------------------------------------------------ // // Returns string containing info about the object at position (px,py). // Returned string will be re-used (lock in MT environment). // char *MHCamera::GetObjectInfo(Int_t px, Int_t py) const { if (TString(GetDrawOption()).Contains("hist", TString::kIgnoreCase)) return TH1D::GetObjectInfo(px, py); static char info[128]; const Int_t idx=GetPixelIndex(px, py); if (idx<0) return TObject::GetObjectInfo(px, py); sprintf(info, "Software Pixel Index: %d (Hardware Id=%d)", idx, idx+1); return info; } // ------------------------------------------------------------------------ // // Execute a mouse event on the camera // void MHCamera::ExecuteEvent(Int_t event, Int_t px, Int_t py) { if (TString(GetDrawOption()).Contains("hist", TString::kIgnoreCase)) { TH1D::ExecuteEvent(event, px, py); return; } //if (event==kMouseMotion && fStatusBar) // fStatusBar->SetText(GetObjectInfo(px, py), 0); if (event!=kButton1Down) return; const Int_t idx = GetPixelIndex(px, py); if (idx<0) return; cout << GetTitle() << " <" << GetName() << ">" << endl; cout << "Software Pixel Index: " << idx << endl; cout << "Hardware Pixel Id: " << idx+1 << endl; cout << "Contents: " << fArray[idx+1] << " <"; cout << (IsUsed(idx)?"on":"off"); cout << ">" << endl; if (fNotify && fNotify->GetSize()>0) new TCanvas; fNotify->ForEach(MCamEvent, DrawPixelContent)(idx); }