source: trunk/Mars/mhist/MHCamera.cc@ 19797

Last change on this file since 19797 was 19652, checked in by tbretz, 5 years ago
Fixed a surprisingly old bug. The legend was wrong.
File size: 73.6 KB
Line 
1/* ======================================================================== *\
2!
3! *
4! * This file is part of MARS, the MAGIC Analysis and Reconstruction
5! * Software. It is distributed to you in the hope that it can be a useful
6! * and timesaving tool in analysing Data of imaging Cerenkov telescopes.
7! * It is distributed WITHOUT ANY WARRANTY.
8! *
9! * Permission to use, copy, modify and distribute this software and its
10! * documentation for any purpose is hereby granted without fee,
11! * provided that the above copyright notice appear in all copies and
12! * that both that copyright notice and this permission notice appear
13! * in supporting documentation. It is provided "as is" without express
14! * or implied warranty.
15! *
16!
17!
18! Author(s): Thomas Bretz, 05/2002 <mailto:tbretz@astro.uni-wuerzburg.de>
19! Author(s): Harald Kornmayer, 1/2001
20! Author(s): Markus Gaug, 03/2004 <mailto:markus@ifae.es>
21!
22! Copyright: MAGIC Software Development, 2000-2008
23!
24!
25\* ======================================================================== */
26
27/////////////////////////////////////////////////////////////////////////////
28//
29// MHCamera
30//
31// Camera Display, based on a TH1D. Pleas be carefull using the
32// underlaying TH1D.
33//
34// To change the scale to a logarithmic scale SetLogy() of the Pad.
35//
36// You can correct for the abberation. Assuming that the distance
37// between the mean position of the light distribution and the position
38// of a perfect reflection on a perfect mirror in the distance r on
39// the camera plane is dr it is d = a*dr while a is the abberation
40// constant (for the MAGIC mirror it is roughly 0.0713). You can
41// set this constant by calling SetAbberation(a) which results in a
42// 'corrected' display (all outer pixels are shifted towards the center
43// of the camera to correct for this abberation)
44//
45// Be carefull: Entries in this context means Entries/bin or Events
46//
47// FIXME? Maybe MHCamera can take the fLog object from MGeomCam?
48//
49////////////////////////////////////////////////////////////////////////////
50#include "MHCamera.h"
51
52#include <fstream>
53#include <iostream>
54
55#include <TBox.h>
56#include <TArrow.h>
57#include <TLatex.h>
58#include <TStyle.h>
59#include <TCanvas.h>
60#include <TArrayF.h>
61#include <TRandom.h>
62#include <TPaveText.h>
63#include <TPaveStats.h>
64#include <TClonesArray.h>
65#include <THistPainter.h>
66#include <THLimitsFinder.h>
67#include <TProfile.h>
68#include <TH1.h>
69#include <TF1.h>
70#include <TCanvas.h>
71#include <TLegend.h>
72
73#include "MLog.h"
74#include "MLogManip.h"
75
76#include "MH.h"
77#include "MString.h"
78#include "MBinning.h"
79
80#include "MGeom.h"
81#include "MGeomCam.h"
82
83#include "MCamEvent.h"
84
85#include "MArrayD.h"
86#include "MMath.h" // MMath::GaussProb
87
88ClassImp(MHCamera);
89
90using namespace std;
91
92void MHCamera::Init()
93{
94 Sumw2();
95
96 UseCurrentStyle();
97
98 SetDirectory(NULL);
99
100 SetLineColor(kGreen);
101 SetMarkerStyle(kFullDotMedium);
102 SetXTitle("Pixel Index");
103
104 fNotify = new TList;
105 fNotify->SetBit(kMustCleanup);
106 gROOT->GetListOfCleanups()->Add(fNotify);
107
108 /*
109 TVirtualPad *save = gPad;
110 gPad = 0;
111#if ROOT_VERSION_CODE < ROOT_VERSION(3,01,06)
112 SetPalette(1, 0);
113#endif
114 */
115/*
116#if ROOT_VERSION_CODE < ROOT_VERSION(4,04,00)
117 SetPrettyPalette();
118#elese
119 // WORAROUND - FIXME: Calling it many times becomes slower and slower
120 SetInvDeepBlueSeaPalette();
121#endif
122 gPad = save;
123*/
124}
125
126// ------------------------------------------------------------------------
127//
128// Default Constructor. To be used by the root system ONLY.
129//
130MHCamera::MHCamera() : TH1D(), fGeomCam(NULL), fAbberation(0)
131{
132 Init();
133}
134
135// ------------------------------------------------------------------------
136//
137// Constructor. Makes a clone of MGeomCam. Removed the TH1D from the
138// current directory. Calls Sumw2(). Set the histogram line color
139// (for error bars) to Green and the marker style to kFullDotMedium.
140//
141MHCamera::MHCamera(const MGeomCam &geom, const char *name, const char *title)
142: fGeomCam(NULL), fAbberation(0)
143{
144 //fGeomCam = (MGeomCam*)geom.Clone();
145 SetGeometry(geom, name, title);
146 Init();
147
148 //
149 // root 3.02
150 // * base/inc/TObject.h:
151 // register BIT(8) as kNoContextMenu. If an object has this bit set it will
152 // not get an automatic context menu when clicked with the right mouse button.
153}
154
155// ------------------------------------------------------------------------
156//
157// Clone the MHCamera via TH1D::Clone and make sure that the new object is
158// not removed from the current directory.
159//
160TObject *MHCamera::Clone(const char *newname) const
161{
162 MHCamera *rc = static_cast<MHCamera*>(TH1D::Clone(newname));
163 rc->SetDirectory(NULL);
164
165 // fGeomCam need special treatment due to its TObjArray
166 if (rc->fGeomCam && fGeomCam)
167 {
168 delete rc->fGeomCam;
169 rc->fGeomCam = static_cast<MGeomCam*>(fGeomCam->Clone());
170 }
171
172 return rc;
173}
174
175void MHCamera::SetGeometry(const MGeomCam &geom, const char *name, const char *title)
176{
177 SetNameTitle(name, title);
178
179 TAxis &x = *GetXaxis();
180
181 SetBins(geom.GetNumPixels(), 0, 1);
182 x.Set(geom.GetNumPixels(), -0.5, geom.GetNumPixels()-0.5);
183
184 //SetBins(geom.GetNumPixels(), -0.5, geom.GetNumPixels()-0.5);
185 //Rebuild();
186
187 if (fGeomCam)
188 delete fGeomCam;
189 fGeomCam = (MGeomCam*)geom.Clone();
190
191 fUsed.Set(geom.GetNumPixels());
192 fUsed.Reset();
193
194 fBinEntries.Set(geom.GetNumPixels()+2);
195 fBinEntries.Reset();
196}
197
198// ------------------------------------------------------------------------
199//
200// Destructor. Deletes the cloned fGeomCam and the notification list.
201//
202MHCamera::~MHCamera()
203{
204 if (fGeomCam)
205 {
206 delete fGeomCam;
207 fGeomCam = 0;
208 }
209 if (fNotify)
210 {
211 delete fNotify;
212 fNotify = 0;
213 }
214}
215
216// ------------------------------------------------------------------------
217//
218// Return kTRUE for sector<0. Otherwise return kTRUE only if the specified
219// sector idx matches the sector of the pixel with index idx.
220//
221Bool_t MHCamera::MatchSector(Int_t idx, const TArrayI &sector, const TArrayI &aidx) const
222{
223 const MGeom &pix = (*fGeomCam)[idx];
224 return FindVal(sector, pix.GetSector()) && FindVal(aidx, pix.GetAidx());
225}
226
227// ------------------------------------------------------------------------
228//
229// Taken from TH1D::Fill(). Uses the argument directly as bin index.
230// Doesn't increment the number of entries.
231//
232// -*-*-*-*-*-*-*-*Increment bin with abscissa X by 1*-*-*-*-*-*-*-*-*-*-*
233// ==================================
234//
235// if x is less than the low-edge of the first bin, the Underflow bin is incremented
236// if x is greater than the upper edge of last bin, the Overflow bin is incremented
237//
238// If the storage of the sum of squares of weights has been triggered,
239// via the function Sumw2, then the sum of the squares of weights is incremented
240// by 1 in the bin corresponding to x.
241//
242// -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
243Int_t MHCamera::Fill(Axis_t x)
244{
245#if ROOT_VERSION_CODE > ROOT_VERSION(3,05,00)
246 if (fBuffer) return BufferFill(x,1);
247#endif
248 const Int_t bin = (Int_t)x+1;
249 AddBinContent(bin);
250 fBinEntries[bin]++;
251 if (fSumw2.fN)
252 fSumw2.fArray[bin]++;
253
254 if (bin<=0 || bin>fNcells-2)
255 return -1;
256
257 fTsumw++;
258 fTsumw2++;
259 fTsumwx += x;
260 fTsumwx2 += x*x;
261 return bin;
262}
263
264// ------------------------------------------------------------------------
265//
266// Taken from TH1D::Fill(). Uses the argument directly as bin index.
267// Doesn't increment the number of entries.
268//
269// -*-*-*-*-*-*Increment bin with abscissa X with a weight w*-*-*-*-*-*-*-*
270// =============================================
271//
272// if x is less than the low-edge of the first bin, the Underflow bin is incremented
273// if x is greater than the upper edge of last bin, the Overflow bin is incremented
274//
275// If the storage of the sum of squares of weights has been triggered,
276// via the function Sumw2, then the sum of the squares of weights is incremented
277// by w^2 in the bin corresponding to x.
278//
279// -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
280Int_t MHCamera::Fill(Axis_t x, Stat_t w)
281{
282#if ROOT_VERSION_CODE > ROOT_VERSION(3,05,00)
283 if (fBuffer) return BufferFill(x,w);
284#endif
285 const Int_t bin = (Int_t)x+1;
286 if (bin<0 || bin>fNcells-1)
287 return -1;
288
289 AddBinContent(bin, w);
290 fBinEntries[bin]++;
291 if (fSumw2.fN)
292 fSumw2.fArray[bin] += w*w;
293
294 if (bin<=0 || bin>fNcells-2)
295 return -1;
296
297 const Stat_t z = (w > 0 ? w : -w);
298 fTsumw += z;
299 fTsumw2 += z*z;
300 fTsumwx += z*x;
301 fTsumwx2 += z*x*x;
302 return bin;
303}
304
305// ------------------------------------------------------------------------
306//
307// Use x and y in millimeters
308//
309Int_t MHCamera::Fill(Axis_t x, Axis_t y, Stat_t w)
310{
311 if (fNcells<=1 || IsFreezed())
312 return -1;
313
314 for (Int_t idx=0; idx<fNcells-2; idx++)
315 {
316 if (!(*fGeomCam)[idx].IsInside(x, y))
317 continue;
318
319 SetUsed(idx);
320 return Fill(idx, w);
321 }
322 return -1;
323}
324
325// ------------------------------------------------------------------------
326//
327// Call this if you want to change the display status (displayed or not)
328// for all pixels. val==0 means that the pixel is not displayed.
329//
330void MHCamera::SetUsed(const TArrayC &arr)
331{
332 if (fNcells-2 != arr.GetSize())
333 {
334 gLog << warn << "WARNING - MHCamera::SetUsed: array size mismatch... ignored." << endl;
335 return;
336 }
337
338 for (Int_t idx=0; idx<fNcells-2; idx++)
339 arr[idx] ? SetUsed(idx) : ResetUsed(idx);
340}
341
342// ------------------------------------------------------------------------
343//
344// Return the mean value of all entries which are used if all=kFALSE and
345// of all entries if all=kTRUE if sector<0. If sector>=0 only
346// entries with match the given sector are taken into account.
347//
348Stat_t MHCamera::GetMeanSectors(const TArrayI &sector, const TArrayI &aidx, Bool_t ball) const
349{
350 if (fNcells<=1)
351 return 0;
352
353 Int_t n=0;
354
355 Stat_t mean = 0;
356 for (int i=0; i<fNcells-2; i++)
357 {
358 if ((ball || IsUsed(i)) && MatchSector(i, sector, aidx))
359 {
360 if (TestBit(kProfile) && fBinEntries[i+1]==0)
361 continue;
362
363 mean += TestBit(kProfile) ? fArray[i+1]/fBinEntries[i+1] : fArray[i+1];
364 n++;
365 }
366 }
367
368 return n==0 ? 0 : mean/n;
369}
370
371UInt_t MHCamera::GetNumUsedSector(const TArrayI &sector, const TArrayI &aidx) const
372{
373 if (fNcells<=1)
374 return 0;
375
376 Int_t n=0;
377
378 for (int i=0; i<fNcells-2; i++)
379 {
380 if (!IsUsed(i) || !MatchSector(i, sector, aidx))
381 continue;
382
383 if (TestBit(kProfile) && fBinEntries[i+1]==0)
384 continue;
385 n++;
386 }
387
388 // return Median of the profile data
389 return n;
390}
391
392// ------------------------------------------------------------------------
393//
394// Return the median value of all entries which are used if all=kFALSE and
395// of all entries if all=kTRUE if sector<0. If sector>=0 only
396// entries with match the given sector are taken into account.
397//
398Stat_t MHCamera::GetMedianSectors(const TArrayI &sector, const TArrayI &aidx, Bool_t ball) const
399{
400 if (fNcells<=1)
401 return 0;
402
403 TArrayD arr(fNcells-2);
404 Int_t n=0;
405
406 for (int i=0; i<fNcells-2; i++)
407 {
408 if ((ball || IsUsed(i)) && MatchSector(i, sector, aidx))
409 {
410 if (TestBit(kProfile) && fBinEntries[i+1]==0)
411 continue;
412
413 arr[n++] = TestBit(kProfile) ? fArray[i+1]/fBinEntries[i+1] : fArray[i+1];
414 }
415 }
416
417 // return Median of the profile data
418 return TMath::Median(n, arr.GetArray());
419}
420
421// ------------------------------------------------------------------------
422//
423// Return the sqrt variance of all entries which are used if all=kFALSE and
424// of all entries if all=kTRUE if sector<0. If sector>=0 only
425// entries with match the given sector are taken into account.
426//
427Stat_t MHCamera::GetRmsSectors(const TArrayI &sector, const TArrayI &aidx, Bool_t ball) const
428{
429 if (fNcells<=1)
430 return -1;
431
432 Int_t n=0;
433
434 Stat_t sum = 0;
435 Stat_t sq = 0;
436 for (int i=0; i<fNcells-2; i++)
437 {
438 if ((ball || IsUsed(i)) && MatchSector(i, sector, aidx))
439 {
440 if (TestBit(kProfile) && fBinEntries[i+1]==0)
441 continue;
442
443 const Double_t val = TestBit(kProfile) ? fArray[i+1]/fBinEntries[i+1] : fArray[i+1];
444
445 sum += val;
446 sq += val*val;
447 n++;
448 }
449 }
450
451 if (n==0)
452 return 0;
453
454 sum /= n;
455 sq /= n;
456
457 return TMath::Sqrt(sq-sum*sum);
458}
459
460// ------------------------------------------------------------------------
461//
462// Return the median value (divided by MMath::GausProb(1.0)=68.3%) of the
463// distribution of abs(y[i]-Median). This is my Median equivalent of the RMS.
464// Return the deviation of all entries which are used if all=kFALSE and
465// of all entries if all=kTRUE if sector<0. If sector>=0 only
466// entries with match the given sector are taken into account.
467//
468Stat_t MHCamera::GetDevSectors(const TArrayI &sector, const TArrayI &aidx, Bool_t ball) const
469{
470 if (fNcells<=1)
471 return 0;
472
473 TArrayD arr(fNcells-2);
474 Int_t n=0;
475
476 for (int i=0; i<fNcells-2; i++)
477 {
478 if ((ball || IsUsed(i)) && MatchSector(i, sector, aidx))
479 {
480 if (TestBit(kProfile) && fBinEntries[i+1]==0)
481 continue;
482
483 arr[n++] = TestBit(kProfile) ? fArray[i+1]/fBinEntries[i+1] : fArray[i+1];
484 }
485 }
486
487 // return Median of the profile data
488 return MMath::MedianDev(n, arr.GetArray());
489}
490
491// ------------------------------------------------------------------------
492//
493// Return the minimum contents of all pixels (if all is set, otherwise
494// only of all 'used' pixels), fMinimum if fMinimum set. If sector>=0
495// only pixels with matching sector number are taken into account.
496//
497Double_t MHCamera::GetMinimumSectors(const TArrayI &sector, const TArrayI &aidx, Bool_t ball) const
498{
499 if (fMinimum != -1111)
500 return fMinimum;
501
502 if (fNcells<=1)
503 return 0;
504
505 Double_t minimum=FLT_MAX;
506
507 for (Int_t i=0; i<fNcells-2; i++)
508 {
509 if (TestBit(kProfile) && fBinEntries[i+1]==0)
510 continue;
511
512 const Double_t val = TestBit(kProfile) ? fArray[i+1]/fBinEntries[i+1] : fArray[i+1];
513 if (MatchSector(i, sector, aidx) && (ball || IsUsed(i)) && val<minimum && TMath::Finite(val))
514 minimum = val;
515 }
516
517 return minimum;
518}
519
520// ------------------------------------------------------------------------
521//
522// Return the maximum contents of all pixels (if all is set, otherwise
523// only of all 'used' pixels), fMaximum if fMaximum set. If sector>=0
524// only pixels with matching sector number are taken into account.
525//
526Double_t MHCamera::GetMaximumSectors(const TArrayI &sector, const TArrayI &aidx, Bool_t ball) const
527{
528 if (fMaximum!=-1111)
529 return fMaximum;
530
531 if (fNcells<=1)
532 return 1;
533
534 Double_t maximum=-FLT_MAX;
535 for (Int_t i=0; i<fNcells-2; i++)
536 {
537 if (TestBit(kProfile) && fBinEntries[i+1]==0)
538 continue;
539
540 const Double_t val = TestBit(kProfile) ? fArray[i+1]/fBinEntries[i+1] : fArray[i+1];
541 if (MatchSector(i, sector, aidx) && (ball || IsUsed(i)) && val>maximum && TMath::Finite(val))
542 maximum = val;
543 }
544
545 return maximum;
546}
547
548// ------------------------------------------------------------------------
549//
550// Return the number of bins (excluding under- and overflow) for which
551// GetBinContent returns a value > t
552//
553Int_t MHCamera::GetNumBinsAboveThreshold(Double_t t) const
554{
555 Int_t n = 0;
556 for (Int_t i=0; i<fNcells-2; i++)
557 if (GetBinContent(i+1)>t)
558 n++;
559
560 return n;
561}
562
563// ------------------------------------------------------------------------
564//
565// Return the number of bins (excluding under- and overflow) for which
566// GetBinContent returns a value < t
567//
568Int_t MHCamera::GetNumBinsBelowThreshold(Double_t t) const
569{
570 Int_t n = 0;
571 for (Int_t i=0; i<fNcells-2; i++)
572 if (GetBinContent(i+1)>t)
573 n++;
574
575 return n;
576}
577
578// ------------------------------------------------------------------------
579//
580// Call this function to draw the camera layout into your canvas.
581// Setup a drawing canvas. Add this object and all child objects
582// (hexagons, etc) to the current pad. If no pad exists a new one is
583// created. (To access the 'real' pad containing the camera you have
584// to do a cd(1) in the current layer.
585//
586// To draw a camera into its own pad do something like:
587//
588// MGeomCamMagic m;
589// MHCamera *d=new MHCamera(m);
590//
591// TCanvas *c = new TCanvas;
592// c->Divide(2,1);
593// c->cd(1);
594//
595// d->FillRandom();
596// d->Draw();
597// d->SetBit(kCanDelete);
598//
599// There are several drawing options:
600// 'hist' Draw as a standard TH1 histogram (value vs. pixel index)
601// 'box' Draw hexagons which size is in respect to its contents
602// 'nocol' Leave the 'boxed' hexagons empty
603// 'pixelindex' Display the pixel index in each pixel
604// 'sectorindex' Display the sector index in each pixel
605// 'content' Display the relative content aligned to GetMaximum() and
606// GeMinimum() ((val-min)/(max-min))
607// 'text' Draw GetBinContent as char
608// 'integer' Draw GetBinContent as integer (TMath::Nint)
609// 'float' Draw GetBinContent as float
610// 'proj' Display the y-projection of the histogram
611// 'pal0' Use Pretty palette
612// 'pal1' Use Deep Blue Sea palette
613// 'pal2' Use Inverse Depp Blue Sea palette
614// 'same' Draw trandparent pixels on top of an existing pad. This
615// makes it possible to draw the camera image on top of an
616// existing TH2, but also allows for distorted camera images
617//
618void MHCamera::Draw(Option_t *option)
619{
620 const Bool_t hassame = TString(option).Contains("same", TString::kIgnoreCase) && gPad;
621
622 // root 3.02:
623 // gPad->SetFixedAspectRatio()
624 const Color_t col = gPad ? gPad->GetFillColor() : 16;
625 TVirtualPad *pad = gPad ? gPad : MH::MakeDefCanvas("CamDisplay", "Mars Camera Display", 656, 600);
626
627 if (!hassame)
628 {
629 pad->SetBorderMode(0);
630 pad->SetFillColor(col);
631
632 //
633 // Create an own pad for the MHCamera-Object which can be
634 // resized in paint to keep the correct aspect ratio
635 //
636 // The margin != 0 is a workaround for a problem in root 4.02/00
637 //pad->Divide(1, 1, 1e-10, 1e-10, col);
638 //pad->cd(1);
639 //gPad->SetBorderMode(0);
640 }
641
642 AppendPad(option);
643 //fGeomCam->AppendPad();
644
645 //
646 // Do not change gPad. The user should not see, that Draw
647 // changes gPad...
648 //
649// if (!hassame)
650// pad->cd();
651}
652
653// ------------------------------------------------------------------------
654//
655// Creates a TH1D which contains the projection of the contents of the
656// MHCamera onto the y-axis. The maximum and minimum are calculated
657// such that a slighly wider range than (GetMinimum(), GetMaximum()) is
658// displayed using THLimitsFinder::OptimizeLimits.
659//
660// If no name is given the newly allocated histogram is removed from
661// the current directory calling SetDirectory(0) in any other case
662// the newly created histogram is removed from the current directory
663// and added to gROOT such the gROOT->FindObject can find the histogram.
664//
665// If the standard name "_proj" is given "_proj" is appended to the name
666// of the MHCamera and the corresponding histogram is searched using
667// gROOT->FindObject and updated with the present projection.
668//
669// It is the responsibility of the user to make sure, that the newly
670// created histogram is freed correctly.
671//
672// Currently the new histogram is restrictred to 50 bins.
673// Maybe a optimal number can be calulated from the number of
674// bins on the x-axis of the MHCamera?
675//
676// The code was taken mainly from TH2::ProjectX such the interface
677// is more or less the same than to TH2-projections.
678//
679// If sector>=0 only entries with matching sector index are taken
680// into account.
681//
682TH1D *MHCamera::ProjectionS(const TArrayI &sector, const TArrayI &aidx, const char *name, const Int_t nbins) const
683{
684
685 // Create the projection histogram
686 TString pname(name);
687 if (pname=="_proj")
688 {
689 pname.Prepend(GetName());
690 if (sector.GetSize()>0)
691 {
692 pname += ";";
693 for (int i=0; i<sector.GetSize(); i++)
694 pname += sector[i];
695 }
696 if (aidx.GetSize()>0)
697 {
698 pname += ";";
699 for (int i=0; i<aidx.GetSize(); i++)
700 pname += aidx[i];
701 }
702 }
703
704 TH1D *h1=0;
705
706 //check if histogram with identical name exist
707 TObject *h1obj = gROOT->FindObject(pname);
708 if (h1obj && h1obj->InheritsFrom(TH1D::Class())) {
709 h1 = (TH1D*)h1obj;
710 h1->Reset();
711 }
712
713 if (!h1)
714 {
715 h1 = new TH1D;
716 h1->UseCurrentStyle();
717 h1->SetName(pname);
718 h1->SetTitle(GetTitle());
719 h1->SetDirectory(0);
720 h1->SetXTitle(GetYaxis()->GetTitle());
721 h1->SetYTitle("Counts");
722 //h1->Sumw2();
723 }
724
725 Double_t min = GetMinimumSectors(sector, aidx);
726 Double_t max = GetMaximumSectors(sector, aidx);
727
728 if (min==max && max>0)
729 min=0;
730 if (min==max && min<0)
731 max=0;
732
733 Int_t newbins=0;
734 THLimitsFinder::OptimizeLimits(nbins, newbins, min, max, kFALSE);
735
736 MBinning bins(nbins, min, max);
737 bins.Apply(*h1);
738
739 // Fill the projected histogram
740 for (Int_t idx=0; idx<fNcells-2; idx++)
741 if (IsUsed(idx) && MatchSector(idx, sector, aidx) && TMath::Finite(GetBinContent(idx+1)))
742 h1->Fill(GetBinContent(idx+1));
743
744 return h1;
745}
746
747// ------------------------------------------------------------------------
748//
749// Creates a TH1D which contains the projection of the contents of the
750// MHCamera onto the radius from the camera center.
751// The maximum and minimum are calculated
752// such that a slighly wider range than (GetMinimum(), GetMaximum()) is
753// displayed using THLimitsFinder::OptimizeLimits.
754//
755// If no name is given the newly allocated histogram is removed from
756// the current directory calling SetDirectory(0) in any other case
757// the newly created histogram is removed from the current directory
758// and added to gROOT such the gROOT->FindObject can find the histogram.
759//
760// If the standard name "_rad" is given "_rad" is appended to the name
761// of the MHCamera and the corresponding histogram is searched using
762// gROOT->FindObject and updated with the present projection.
763//
764// It is the responsibility of the user to make sure, that the newly
765// created histogram is freed correctly.
766//
767// Currently the new histogram is restrictred to 50 bins.
768// Maybe a optimal number can be calulated from the number of
769// bins on the x-axis of the MHCamera?
770//
771// The code was taken mainly from TH2::ProjectX such the interface
772// is more or less the same than to TH2-projections.
773//
774// If sector>=0 only entries with matching sector index are taken
775// into account.
776//
777TProfile *MHCamera::RadialProfileS(const TArrayI &sector, const TArrayI &aidx, const char *name, const Int_t nbins) const
778{
779 // Create the projection histogram
780 TString pname(name);
781 if (pname=="_rad")
782 {
783 pname.Prepend(GetName());
784 if (sector.GetSize()>0)
785 {
786 pname += ";";
787 for (int i=0; i<sector.GetSize(); i++)
788 pname += sector[i];
789 }
790 if (aidx.GetSize()>0)
791 {
792 pname += ";";
793 for (int i=0; i<aidx.GetSize(); i++)
794 pname += aidx[i];
795 }
796 }
797
798 TProfile *h1=0;
799
800 //check if histogram with identical name exist
801 TObject *h1obj = gROOT->FindObject(pname);
802 if (h1obj && h1obj->InheritsFrom(TProfile::Class())) {
803 h1 = (TProfile*)h1obj;
804 h1->Reset();
805 }
806
807 if (!h1)
808 {
809 h1 = new TProfile;
810 h1->UseCurrentStyle();
811 h1->SetName(pname);
812 h1->SetTitle(GetTitle());
813 h1->SetDirectory(0);
814 h1->SetXTitle("Radius from camera center [deg]");
815 h1->SetYTitle(GetYaxis()->GetTitle());
816 }
817
818 const Double_t m2d = fGeomCam->GetConvMm2Deg();
819
820 Double_t min = 0.;
821 Double_t max = fGeomCam->GetMaxRadius()*m2d;
822
823 Int_t newbins=0;
824
825 THLimitsFinder::OptimizeLimits(nbins, newbins, min, max, kFALSE);
826
827 MBinning bins(nbins, min, max);
828 bins.Apply(*h1);
829
830 // Fill the projected histogram
831 for (Int_t idx=0; idx<fNcells-2; idx++)
832 if (IsUsed(idx) && MatchSector(idx, sector, aidx) && TMath::Finite(GetBinContent(idx+1)))
833 h1->Fill(TMath::Hypot((*fGeomCam)[idx].GetX(),(*fGeomCam)[idx].GetY())*m2d,
834 GetBinContent(idx+1));
835 return h1;
836}
837
838
839// ------------------------------------------------------------------------
840//
841// Creates a TH1D which contains the projection of the contents of the
842// MHCamera onto the azimuth angle in the camera.
843//
844// If no name is given the newly allocated histogram is removed from
845// the current directory calling SetDirectory(0) in any other case
846// the newly created histogram is removed from the current directory
847// and added to gROOT such the gROOT->FindObject can find the histogram.
848//
849// If the standard name "_az" is given "_az" is appended to the name
850// of the MHCamera and the corresponding histogram is searched using
851// gROOT->FindObject and updated with the present projection.
852//
853// It is the responsibility of the user to make sure, that the newly
854// created histogram is freed correctly.
855//
856// Currently the new histogram is restrictred to 60 bins.
857// Maybe a optimal number can be calulated from the number of
858// bins on the x-axis of the MHCamera?
859//
860// The code was taken mainly from TH2::ProjectX such the interface
861// is more or less the same than to TH2-projections.
862//
863TProfile *MHCamera::AzimuthProfileA(const TArrayI &aidx, const char *name, const Int_t nbins) const
864{
865 // Create the projection histogram
866 TString pname(name);
867 if (pname=="_az")
868 {
869 pname.Prepend(GetName());
870 if (aidx.GetSize()>0)
871 {
872 pname += ";";
873 for (int i=0; i<aidx.GetSize(); i++)
874 pname += aidx[i];
875 }
876 }
877
878 TProfile *h1=0;
879
880 //check if histogram with identical name exist
881 TObject *h1obj = gROOT->FindObject(pname);
882 if (h1obj && h1obj->InheritsFrom(TProfile::Class())) {
883 h1 = (TProfile*)h1obj;
884 h1->Reset();
885 }
886
887 if (!h1)
888 {
889
890 h1 = new TProfile;
891 h1->UseCurrentStyle();
892 h1->SetName(pname);
893 h1->SetTitle(GetTitle());
894 h1->SetDirectory(0);
895 h1->SetXTitle("Azimuth in camera [deg]");
896 h1->SetYTitle(GetYaxis()->GetTitle());
897 }
898
899 //Double_t min = 0;
900 //Double_t max = 360;
901
902 //Int_t newbins=0;
903 //THLimitsFinder::OptimizeLimits(nbins, newbins, min, max, kFALSE);
904
905 MBinning bins(nbins, 0, 360);
906 bins.Apply(*h1);
907
908 // Fill the projected histogram
909 for (Int_t idx=0; idx<fNcells-2; idx++)
910 {
911 if (IsUsed(idx) && MatchSector(idx, TArrayI(), aidx) && TMath::Finite(GetPixContent(idx)))
912 h1->Fill(TMath::ATan2((*fGeomCam)[idx].GetY(),(*fGeomCam)[idx].GetX())*TMath::RadToDeg()+180,
913 GetPixContent(idx));
914
915 }
916
917 return h1;
918}
919
920// ------------------------------------------------------------------------
921//
922// Updates the pixel colors and paints the pixels
923//
924void MHCamera::Update(Bool_t islog, Bool_t isbox, Bool_t iscol, Bool_t issame)
925{
926 Double_t min = GetMinimum(kFALSE);
927 Double_t max = GetMaximum(kFALSE);
928 if (min==FLT_MAX)
929 {
930 min = 0;
931 max = 1;
932 }
933
934 if (min==max)
935 max += 1;
936
937 if (!issame)
938 UpdateLegend(min, max, islog);
939
940 // Try to estimate the units of the current display. This is only
941 // necessary for 'same' option and allows distorted images of the camera!
942 const Float_t maxr = (1-fGeomCam->GetConvMm2Deg())*fGeomCam->GetMaxRadius()/2;
943 const Float_t conv = !issame ||
944 gPad->GetX1()<-maxr || gPad->GetY1()<-maxr ||
945 gPad->GetX2()> maxr || gPad->GetY2()>maxr ? 1 : fGeomCam->GetConvMm2Deg();
946
947 TAttLine line(kBlack, kSolid, 1);
948 TAttFill fill;
949 for (Int_t i=0; i<fNcells-2; i++)
950 {
951 fill.SetFillStyle(issame || (IsTransparent() && !IsUsed(i)) ? 0 : 1001);
952
953 if (!issame)
954 {
955 const Bool_t isnan = !TMath::Finite(fArray[i+1]);
956 if (!IsUsed(i) || !iscol || isnan)
957 {
958 fill.SetFillColor(10);
959
960 if (isnan)
961 gLog << warn << "MHCamera::Update: " << GetName() << " <" << GetTitle() << "> - Pixel Index #" << i << " contents is not finite..." << endl;
962 }
963 else
964 fill.SetFillColor(GetColor(GetBinContent(i+1), min, max, islog));
965 }
966
967 const MGeom &pix = (*fGeomCam)[i];
968
969 Double_t scale = 1;//conv/(fAbberation+1);
970
971 if (!isbox && !IsUsed(i) && TestBit(kNoUnused))
972 continue;
973
974 if (isbox && (!IsUsed(i) || !TMath::Finite(fArray[i+1])))
975 continue;
976
977 if (isbox)
978 {
979 scale = (GetBinContent(i+1)-min)/(max-min);
980 if (scale>1)
981 scale=1;
982 }
983
984 pix.PaintPrimitive(line, fill, conv, scale/(fAbberation+1));
985 }
986}
987
988// ------------------------------------------------------------------------
989//
990// Print minimum and maximum
991//
992void MHCamera::Print(Option_t *) const
993{
994 gLog << all << "Minimum: " << GetMinimum();
995 if (fMinimum==-1111)
996 gLog << " <autoscaled>";
997 gLog << endl;
998 gLog << "Maximum: " << GetMaximum();
999 if (fMaximum==-1111)
1000 gLog << " <autoscaled>";
1001 gLog << endl;
1002}
1003
1004// ------------------------------------------------------------------------
1005//
1006// Paint the y-axis title
1007//
1008void MHCamera::PaintAxisTitle()
1009{
1010 const Float_t range = fGeomCam->GetMaxRadius()*1.05;
1011 const Float_t w = (1 + 1.5/sqrt((float)(fNcells-2)))*range;
1012
1013 TLatex *ptitle = new TLatex(w, -.90*range, GetYaxis()->GetTitle());
1014
1015 ptitle->SetTextSize(0.05);
1016 ptitle->SetTextAlign(21);
1017
1018 // box with the histogram title
1019 ptitle->SetTextColor(gStyle->GetTitleTextColor());
1020#if ROOT_VERSION_CODE > ROOT_VERSION(3,05,01)
1021 ptitle->SetTextFont(gStyle->GetTitleFont(""));
1022#endif
1023 ptitle->Paint();
1024}
1025
1026// ------------------------------------------------------------------------
1027//
1028// Paints the camera.
1029//
1030void MHCamera::Paint(Option_t *o)
1031{
1032 if (fNcells<=1)
1033 return;
1034
1035 TString opt(o);
1036 opt.ToLower();
1037
1038 if (opt.Contains("hist"))
1039 {
1040 opt.ReplaceAll("hist", "");
1041 opt.ReplaceAll("box", "");
1042 opt.ReplaceAll("pixelindex", "");
1043 opt.ReplaceAll("sectorindex", "");
1044 opt.ReplaceAll("abscontent", "");
1045 opt.ReplaceAll("content", "");
1046 opt.ReplaceAll("integer", "");
1047 opt.ReplaceAll("float", "");
1048 opt.ReplaceAll("proj", "");
1049 opt.ReplaceAll("pal0", "");
1050 opt.ReplaceAll("pal1", "");
1051 opt.ReplaceAll("pal2", "");
1052 opt.ReplaceAll("nopal", "");
1053 TH1D::Paint(opt);
1054 return;
1055 }
1056
1057 if (opt.Contains("proj"))
1058 {
1059 opt.ReplaceAll("proj", "");
1060 Projection(GetName())->Paint(opt);
1061 return;
1062 }
1063
1064 const Bool_t hassame = opt.Contains("same");
1065 const Bool_t hasbox = opt.Contains("box");
1066 const Bool_t hascol = hasbox ? !opt.Contains("nocol") : kTRUE;
1067
1068 if (!hassame)
1069 gPad->Clear();
1070
1071 if (!fGeomCam)
1072 return;
1073
1074 if (!hassame)
1075 {
1076 // Maintain aspect ratio
1077 const Float_t r = fGeomCam->GetMaxRadius();
1078
1079 MH::SetPadRange(-r*1.02, -r*1.02, TestBit(kNoLegend) ? r : 1.15*r, r*1.2);
1080
1081 if (GetPainter())
1082 {
1083 // Paint statistics
1084 if (!TestBit(TH1::kNoStats))
1085 fPainter->PaintStat(gStyle->GetOptStat(), NULL);
1086
1087 // Paint primitives (pixels, color legend, photons, ...)
1088 if (fPainter->InheritsFrom(THistPainter::Class()))
1089 {
1090 static_cast<THistPainter*>(fPainter)->MakeChopt("");
1091 static_cast<THistPainter*>(fPainter)->PaintTitle();
1092 }
1093 }
1094 }
1095
1096 const Bool_t pal1 = opt.Contains("pal1");
1097 const Bool_t pal2 = opt.Contains("pal2");
1098 const Bool_t nopal = opt.Contains("nopal");
1099
1100 if (!pal1 && !pal2 && !nopal)
1101 SetPrettyPalette();
1102
1103 if (pal1)
1104 SetDeepBlueSeaPalette();
1105
1106 if (pal2)
1107 SetInvDeepBlueSeaPalette();
1108
1109 // Update Contents of the pixels and paint legend
1110 Update(gPad->GetLogy(), hasbox, hascol, hassame);
1111
1112 if (!hassame)
1113 PaintAxisTitle();
1114
1115 if (opt.Contains("pixelindex"))
1116 {
1117 PaintIndices(0);
1118 return;
1119 }
1120 if (opt.Contains("sectorindex"))
1121 {
1122 PaintIndices(1);
1123 return;
1124 }
1125 if (opt.Contains("abscontent"))
1126 {
1127 PaintIndices(3);
1128 return;
1129 }
1130 if (opt.Contains("content"))
1131 {
1132 PaintIndices(2);
1133 return;
1134 }
1135 if (opt.Contains("pixelentries"))
1136 {
1137 PaintIndices(4);
1138 return;
1139 }
1140 if (opt.Contains("text"))
1141 {
1142 PaintIndices(5);
1143 return;
1144 }
1145 if (opt.Contains("integer"))
1146 {
1147 PaintIndices(6);
1148 return;
1149 }
1150 if (opt.Contains("float"))
1151 {
1152 PaintIndices(7);
1153 return;
1154 }
1155}
1156
1157void MHCamera::SetDrawOption(Option_t *option)
1158{
1159 // This is a workaround. For some reason MHCamera is
1160 // stored in a TObjLink instead of a TObjOptLink
1161 if (!option || !gPad)
1162 return;
1163
1164 TListIter next(gPad->GetListOfPrimitives());
1165 delete gPad->FindObject("Tframe");
1166 TObject *obj;
1167 while ((obj = next()))
1168 if (obj == this && (TString)next.GetOption()!=(TString)option)
1169 {
1170 gPad->GetListOfPrimitives()->Remove(this);
1171 gPad->GetListOfPrimitives()->AddFirst(this, option);
1172 return;
1173 }
1174}
1175
1176// ------------------------------------------------------------------------
1177//
1178// With this function you can change the color palette. For more
1179// information see TStyle::SetPalette. Only palettes with 50 colors
1180// are allowed.
1181// In addition you can use SetPalette(52, 0) to create an inverse
1182// deep blue sea palette
1183//
1184void MHCamera::SetPalette(Int_t ncolors, Int_t *colors)
1185{
1186 //
1187 // If not enough colors are specified skip this.
1188 //
1189 if (ncolors>1 && ncolors<50)
1190 {
1191 gLog << err << "MHCamera::SetPalette: Only default palettes with 50 colors are allowed... ignored." << endl;
1192 return;
1193 }
1194
1195 //
1196 // If ncolors==52 create a reversed deep blue sea palette
1197 //
1198 if (ncolors==52)
1199 {
1200 gStyle->SetPalette(51, NULL);
1201
1202 const Int_t n = GetContour()==0?50:GetContour();
1203
1204 TArrayI c(n);
1205 for (int i=0; i<n; i++)
1206 c[n-i-1] = gStyle->GetColorPalette(i);
1207 gStyle->SetPalette(n, c.GetArray());
1208 }
1209 else
1210 gStyle->SetPalette(ncolors, colors);
1211}
1212
1213
1214// ------------------------------------------------------------------------
1215//
1216// Changes the palette of the displayed camera histogram.
1217//
1218// Change to the right pad first - otherwise GetDrawOption() might fail.
1219//
1220void MHCamera::SetPrettyPalette()
1221{
1222 TString opt(GetDrawOption());
1223
1224 if (!opt.Contains("hist", TString::kIgnoreCase))
1225 SetPalette(1, 0);
1226
1227 opt.ReplaceAll("pal1", "");
1228 opt.ReplaceAll("pal2", "");
1229
1230 SetDrawOption(opt);
1231}
1232
1233// ------------------------------------------------------------------------
1234//
1235// Changes the palette of the displayed camera histogram.
1236//
1237// Change to the right pad first - otherwise GetDrawOption() might fail.
1238//
1239void MHCamera::SetDeepBlueSeaPalette()
1240{
1241 TString opt(GetDrawOption());
1242
1243 if (!opt.Contains("hist", TString::kIgnoreCase))
1244 SetPalette(51, 0);
1245
1246 opt.ReplaceAll("pal1", "");
1247 opt.ReplaceAll("pal2", "");
1248 opt += "pal1";
1249
1250 SetDrawOption(opt);
1251}
1252
1253// ------------------------------------------------------------------------
1254//
1255// Changes the palette of the displayed camera histogram.
1256//
1257// Change to the right pad first - otherwise GetDrawOption() might fail.
1258//
1259void MHCamera::SetInvDeepBlueSeaPalette()
1260{
1261 TString opt(GetDrawOption());
1262
1263 if (!opt.Contains("hist", TString::kIgnoreCase))
1264 SetPalette(52, 0);
1265
1266 opt.ReplaceAll("pal1", "");
1267 opt.ReplaceAll("pal2", "");
1268 opt += "pal2";
1269
1270 SetDrawOption(opt);
1271}
1272
1273// ------------------------------------------------------------------------
1274//
1275// Paint indices (as text) inside the pixels. Depending of the type-
1276// argument we paint:
1277// 0: pixel number
1278// 1: sector number
1279// 2: content
1280// 5: Assume GetBinContent is a char
1281//
1282void MHCamera::PaintIndices(Int_t type)
1283{
1284 if (fNcells<=1)
1285 return;
1286
1287 const Double_t min = GetMinimum();
1288 const Double_t max = GetMaximum();
1289
1290 if (type==2 && max==min)
1291 return;
1292
1293 TText txt;
1294 if (type!=5)
1295 txt.SetTextFont(122);
1296 txt.SetTextAlign(22); // centered/centered
1297
1298 for (Int_t i=0; i<fNcells-2; i++)
1299 {
1300 const MGeom &h = (*fGeomCam)[i];
1301
1302 TString num;
1303 switch (type)
1304 {
1305 case 0: num += i; break;
1306 case 1: num += h.GetSector(); break;
1307 case 2: num += TMath::Nint((fArray[i+1]-min)/(max-min)); break;
1308 case 3: num += TMath::Nint(fArray[i+1]); break;
1309 case 4: num += fBinEntries[i+1]; break;
1310 case 5: num = (char)TMath::Nint(GetBinContent(i+1)); break;
1311 case 6: num += TMath::Nint(GetBinContent(i+1)); break;
1312 case 7: num += GetBinContent(i+1); break;
1313 }
1314
1315 // FIXME: Should depend on the color of the pixel...
1316 //(GetColor(GetBinContent(i+1), min, max, 0));
1317 txt.SetTextColor(kRed);
1318 txt.SetTextSize(0.3*h.GetT()/fGeomCam->GetMaxRadius()/1.05);
1319 txt.PaintText(h.GetX(), h.GetY(), num);
1320 }
1321}
1322
1323// ------------------------------------------------------------------------
1324//
1325// Call this function to add a MCamEvent on top of the present contents.
1326//
1327void MHCamera::AddMeanShift(const MCamEvent &event, Int_t type, Stat_t w)
1328{
1329 if (fNcells<=1 || IsFreezed())
1330 return;
1331
1332 const Double_t mean = event.GetCameraMean(*fGeomCam, type);
1333
1334 // FIXME: Security check missing!
1335 for (Int_t idx=0; idx<fNcells-2; idx++)
1336 {
1337 Double_t val=0;
1338 if (event.GetPixelContent(val, idx, *fGeomCam, type)/* && !IsUsed(idx)*/)
1339 {
1340 SetUsed(idx);
1341 Fill(idx, (val-mean)*w); // FIXME: Slow!
1342 }
1343 }
1344 fEntries++;
1345}
1346
1347// ------------------------------------------------------------------------
1348//
1349// Call this function to add a MCamEvent on top of the present contents.
1350//
1351void MHCamera::AddMedianShift(const MCamEvent &event, Int_t type, Stat_t w)
1352{
1353 if (fNcells<=1 || IsFreezed())
1354 return;
1355
1356 const Double_t median = event.GetCameraMedian(*fGeomCam, type);
1357
1358 // FIXME: Security check missing!
1359 for (Int_t idx=0; idx<fNcells-2; idx++)
1360 {
1361 Double_t val=0;
1362 if (event.GetPixelContent(val, idx, *fGeomCam, type)/* && !IsUsed(idx)*/)
1363 {
1364 SetUsed(idx);
1365 Fill(idx, (val-median)*w); // FIXME: Slow!
1366 }
1367 }
1368 fEntries++;
1369}
1370
1371// ------------------------------------------------------------------------
1372//
1373// Call this function to add a MCamEvent on top of the present contents.
1374//
1375void MHCamera::AddCamContent(const MCamEvent &event, Int_t type, Stat_t w)
1376{
1377 if (fNcells<=1 || IsFreezed())
1378 return;
1379
1380 // FIXME: Security check missing!
1381 for (Int_t idx=0; idx<fNcells-2; idx++)
1382 {
1383 Double_t val=0;
1384 if (event.GetPixelContent(val, idx, *fGeomCam, type)/* && !IsUsed(idx)*/)
1385 {
1386 SetUsed(idx);
1387 Fill(idx, val*w); // FIXME: Slow!
1388 }
1389 }
1390 fEntries++;
1391}
1392
1393// ------------------------------------------------------------------------
1394//
1395void MHCamera::AddCamDifference(const MCamEvent &event1, const MCamEvent &event2, Int_t type, Stat_t weight)
1396{
1397 if (fNcells<=1 || IsFreezed())
1398 return;
1399
1400 // FIXME: Security check missing!
1401 for (Int_t idx=0; idx<fNcells-2; idx++)
1402 {
1403 Double_t val1=0;
1404 if (!event1.GetPixelContent(val1, idx, *fGeomCam, type))
1405 continue;
1406
1407 Double_t val2=0;
1408 if (!event2.GetPixelContent(val2, idx, *fGeomCam, type))
1409 continue;
1410
1411 SetUsed(idx);
1412 Fill(idx, (val1-val2)*weight); // FIXME: Slow!
1413 }
1414 fEntries++;
1415}
1416
1417// ------------------------------------------------------------------------
1418//
1419void MHCamera::AddCamRatio(const MCamEvent &event1, const MCamEvent &event2, Int_t type, Stat_t weight)
1420{
1421 if (fNcells<=1 || IsFreezed())
1422 return;
1423
1424 // FIXME: Security check missing!
1425 for (Int_t idx=0; idx<fNcells-2; idx++)
1426 {
1427 Double_t val1=0;
1428 if (!event1.GetPixelContent(val1, idx, *fGeomCam, type))
1429 continue;
1430
1431 Double_t val2=0;
1432 if (!event2.GetPixelContent(val2, idx, *fGeomCam, type))
1433 continue;
1434
1435 if (val2==0)
1436 continue;
1437
1438 SetUsed(idx);
1439 Fill(idx, weight*val1/val2); // FIXME: Slow!
1440 }
1441 fEntries++;
1442}
1443
1444// ------------------------------------------------------------------------
1445//
1446// Call this function to add a MCamEvent on top of the present contents.
1447//
1448void MHCamera::SetCamError(const MCamEvent &evt, Int_t type)
1449{
1450
1451 if (fNcells<=1 || IsFreezed())
1452 return;
1453
1454 // FIXME: Security check missing!
1455 for (Int_t idx=0; idx<fNcells-2; idx++)
1456 {
1457 Double_t val=0;
1458 if (evt.GetPixelContent(val, idx, *fGeomCam, type)/* && !IsUsed(idx)*/)
1459 SetUsed(idx);
1460
1461 SetBinError(idx+1, val); // FIXME: Slow!
1462 }
1463}
1464
1465Stat_t MHCamera::GetBinContent(Int_t bin) const
1466{
1467 if (fBuffer) ((TH1D*)this)->BufferEmpty();
1468 if (bin < 0) bin = 0;
1469 if (bin >= fNcells) bin = fNcells-1;
1470 if (!fArray) return 0;
1471
1472 if (!TestBit(kProfile))
1473 return Stat_t (fArray[bin]);
1474
1475 if (fBinEntries.fArray[bin] == 0) return 0;
1476 return fArray[bin]/fBinEntries.fArray[bin];
1477}
1478
1479// ------------------------------------------------------------------------
1480//
1481// In the case the kProfile flag is set the spread of the bin is returned.
1482// If you want to have the mean error instead set the kErrorMean bit via
1483// SetBit(kErrorMean) first.
1484//
1485Stat_t MHCamera::GetBinError(Int_t bin) const
1486{
1487 if (!TestBit(kProfile))
1488 return TH1D::GetBinError(bin);
1489
1490 const UInt_t n = (UInt_t)fBinEntries[bin];
1491
1492 if (n==0)
1493 return 0;
1494
1495 const Double_t sqr = fSumw2.fArray[bin] / n;
1496 const Double_t val = fArray[bin] / n;
1497
1498 const Double_t spread = sqr>val*val ? TMath::Sqrt(sqr - val*val) : 0;
1499
1500 return TestBit(kErrorMean) ? spread/TMath::Sqrt(n) : spread;
1501
1502 /*
1503 Double_t rc = 0;
1504 if (TestBit(kSqrtVariance) && GetEntries()>0) // error on the mean
1505 {
1506 const Double_t error = fSumw2.fArray[bin]/GetEntries();
1507 const Double_t val = fArray[bin]/GetEntries();
1508 rc = val*val>error ? 0 : TMath::Sqrt(error - val*val);
1509 }
1510 else
1511 rc = TH1D::GetBinError(bin);
1512
1513 return Profile(rc);*/
1514}
1515
1516// ------------------------------------------------------------------------
1517//
1518// Call this function to add a MHCamera on top of the present contents.
1519// Type:
1520// 0) bin content
1521// 1) errors
1522// 2) rel. errors
1523//
1524void MHCamera::AddCamContent(const MHCamera &d, Int_t type)
1525{
1526 if (fNcells!=d.fNcells || IsFreezed())
1527 return;
1528
1529 // FIXME: Security check missing!
1530 for (Int_t idx=0; idx<fNcells-2; idx++)
1531 if (d.IsUsed(idx))
1532 SetUsed(idx);
1533
1534 switch (type)
1535 {
1536 case 1:
1537 // Under-/Overflow bins not handled!
1538 for (Int_t idx=0; idx<fNcells-2; idx++)
1539 if (d.IsUsed(idx))
1540 Fill(idx, d.GetBinError(idx+1));
1541 fEntries++;
1542 break;
1543 case 2:
1544 // Under-/Overflow bins not handled!
1545 for (Int_t idx=0; idx<fNcells-2; idx++)
1546 if (d.GetBinContent(idx+1)!=0 && d.IsUsed(idx))
1547 Fill(idx, TMath::Abs(d.GetBinError(idx+1)/d.GetBinContent(idx+1)));
1548 fEntries++;
1549 break;
1550 default:
1551 if (TestBit(kProfile)!=d.TestBit(kProfile))
1552 gLog << warn << "WARNING - You have tried to call AddCamContent for two different kind of histograms (kProfile set or not)." << endl;
1553
1554 // environment
1555 fEntries += d.fEntries;
1556 fTsumw += d.fTsumw;
1557 fTsumw2 += d.fTsumw2;
1558 fTsumwx += d.fTsumwx;
1559 fTsumwx2 += d.fTsumwx2;
1560 // Bin contents
1561 for (Int_t idx=1; idx<fNcells-1; idx++)
1562 {
1563 if (!d.IsUsed(idx-1))
1564 continue;
1565
1566 fArray[idx] += d.fArray[idx];
1567 fBinEntries[idx] += d.fBinEntries[idx];
1568 fSumw2.fArray[idx] += d.fSumw2.fArray[idx];
1569 }
1570 // Underflow bin
1571 fArray[0] += d.fArray[0];
1572 fBinEntries[0] += d.fBinEntries[0];
1573 fSumw2.fArray[0] += d.fSumw2.fArray[0];
1574 // Overflow bin
1575 fArray[fNcells-1] += d.fArray[fNcells-1];
1576 fBinEntries[fNcells-1] += d.fBinEntries[fNcells-1];
1577 fSumw2.fArray[fNcells-1] += d.fSumw2.fArray[fNcells-1];
1578 break;
1579/* default:
1580 if (TestBit(kProfile)!=d.TestBit(kProfile))
1581 gLog << warn << "WARNING - You have tried to call AddCamContent for two different kind of histograms (kProfile set or not)." << endl;
1582
1583 for (Int_t idx=0; idx<fNcells-2; idx++)
1584 Fill(idx, d.GetBinContent(idx+1));
1585 break;*/
1586 }
1587 fEntries++;
1588}
1589
1590// ------------------------------------------------------------------------
1591//
1592// Call this function to add a TArrayD on top of the present contents.
1593//
1594void MHCamera::AddCamContent(const TArrayD &event, const TArrayC *used)
1595{
1596 if (event.GetSize()!=fNcells-2 || IsFreezed())
1597 return;
1598
1599 if (used && used->GetSize()!=fNcells-2)
1600 return;
1601
1602 for (Int_t idx=0; idx<fNcells-2; idx++)
1603 {
1604 Fill(idx, event[idx]); // FIXME: Slow!
1605
1606 if (!used || (*used)[idx])
1607 SetUsed(idx);
1608 }
1609 fEntries++;
1610}
1611
1612// ------------------------------------------------------------------------
1613//
1614// Call this function to add a MArrayD on top of the present contents.
1615//
1616void MHCamera::AddCamContent(const MArrayD &event, const TArrayC *used)
1617{
1618 if (event.GetSize()!=(UInt_t)(fNcells-2) || IsFreezed())
1619 return;
1620
1621 if (used && used->GetSize()!=fNcells-2)
1622 return;
1623
1624 for (Int_t idx=0; idx<fNcells-2; idx++)
1625 {
1626 Fill(idx, event[idx]); // FIXME: Slow!
1627
1628 if (!used || (*used)[idx])
1629 SetUsed(idx);
1630 }
1631 fEntries++;
1632}
1633
1634// ------------------------------------------------------------------------
1635//
1636// Call this function to add a MCamEvent on top of the present contents.
1637// 1 is added to each pixel if the contents of MCamEvent>threshold
1638// (in case isabove is set to kTRUE == default)
1639// 1 is added to each pixel if the contents of MCamEvent<threshold
1640// (in case isabove is set to kFALSE)
1641//
1642// in unused pixel is not counted if it didn't fullfill the condition.
1643//
1644void MHCamera::CntCamContent(const MCamEvent &event, Double_t threshold, Int_t type, Bool_t isabove)
1645{
1646 if (fNcells<=1 || IsFreezed())
1647 return;
1648
1649 // FIXME: Security check missing!
1650 for (Int_t idx=0; idx<fNcells-2; idx++)
1651 {
1652 Double_t val=threshold;
1653 const Bool_t rc = event.GetPixelContent(val, idx, *fGeomCam, type);
1654 if (rc)
1655 SetUsed(idx);
1656
1657 const Bool_t cond =
1658 ( isabove && val>threshold) ||
1659 (!isabove && val<threshold);
1660
1661 Fill(idx, rc && cond ? 1 : 0);
1662 }
1663 fEntries++;
1664}
1665
1666// ------------------------------------------------------------------------
1667//
1668// Call this function to add a MCamEvent on top of the present contents.
1669// - the contents of the pixels in event are added to each pixel
1670// if the pixel of thresevt<threshold (in case isabove is set
1671// to kTRUE == default)
1672// - the contents of the pixels in event are added to each pixel
1673// if the pixel of thresevt<threshold (in case isabove is set
1674// to kFALSE)
1675//
1676// in unused pixel is not counted if it didn't fullfill the condition.
1677//
1678void MHCamera::CntCamContent(const MCamEvent &event, Int_t type1, const MCamEvent &thresevt, Int_t type2, Double_t threshold, Bool_t isabove)
1679{
1680 if (fNcells<=1 || IsFreezed())
1681 return;
1682
1683 // FIXME: Security check missing!
1684 for (Int_t idx=0; idx<fNcells-2; idx++)
1685 {
1686 Double_t th=0;
1687 if (!thresevt.GetPixelContent(th, idx, *fGeomCam, type2))
1688 continue;
1689
1690 if ((isabove && th>threshold) || (!isabove && th<threshold))
1691 continue;
1692
1693 Double_t val=th;
1694 if (event.GetPixelContent(val, idx, *fGeomCam, type1))
1695 {
1696 SetUsed(idx);
1697 Fill(idx, val);
1698 }
1699 }
1700 fEntries++;
1701}
1702
1703// ------------------------------------------------------------------------
1704//
1705// Call this function to add a MCamEvent on top of the present contents.
1706// 1 is added to each pixel if the contents of MCamEvent>threshold
1707// (in case isabove is set to kTRUE == default)
1708// 1 is added to each pixel if the contents of MCamEvent<threshold
1709// (in case isabove is set to kFALSE)
1710//
1711// in unused pixel is not counted if it didn't fullfill the condition.
1712//
1713void MHCamera::CntCamContent(const MCamEvent &event, TArrayD threshold, Int_t type, Bool_t isabove)
1714{
1715 if (fNcells<=1 || IsFreezed())
1716 return;
1717
1718 // FIXME: Security check missing!
1719 for (Int_t idx=0; idx<fNcells-2; idx++)
1720 {
1721 Double_t val=threshold[idx];
1722 if (event.GetPixelContent(val, idx, *fGeomCam, type)/* && !IsUsed(idx)*/)
1723 {
1724 SetUsed(idx);
1725
1726 if (val>threshold[idx] && isabove)
1727 Fill(idx);
1728 if (val<threshold[idx] && !isabove)
1729 Fill(idx);
1730 }
1731 }
1732 fEntries++;
1733}
1734
1735// ------------------------------------------------------------------------
1736//
1737// Call this function to add a TArrayD on top of the present contents.
1738// 1 is added to each pixel if the contents of MCamEvent>threshold
1739//
1740void MHCamera::CntCamContent(const TArrayD &event, Double_t threshold, Bool_t ispos)
1741{
1742 if (event.GetSize()!=fNcells-2 || IsFreezed())
1743 return;
1744
1745 for (Int_t idx=0; idx<fNcells-2; idx++)
1746 {
1747 if (event[idx]>threshold)
1748 Fill(idx);
1749
1750 if (!ispos || fArray[idx+1]>0)
1751 SetUsed(idx);
1752 }
1753 fEntries++;
1754}
1755
1756// ------------------------------------------------------------------------
1757//
1758// Call this function to add a MCamEvent on top of the present contents.
1759// 1 is added to each pixel if the contents of MCamEvent>threshold
1760// (in case isabove is set to kTRUE == default)
1761// 1 is added to each pixel if the contents of MCamEvent<threshold
1762// (in case isabove is set to kFALSE)
1763//
1764// in unused pixel is not counted if it didn't fullfill the condition.
1765//
1766void MHCamera::SetMaxCamContent(const MCamEvent &event, Int_t type)
1767{
1768 if (fNcells<=1 || IsFreezed())
1769 return;
1770
1771 // FIXME: Security check missing!
1772 for (Int_t idx=0; idx<fNcells-2; idx++)
1773 {
1774 Double_t val=0;
1775 const Bool_t rc = event.GetPixelContent(val, idx, *fGeomCam, type);
1776 if (!rc)
1777 continue;
1778
1779 if (!IsUsed(idx))
1780 {
1781 fArray[idx+1] = val;
1782 SetUsed(idx);
1783 fBinEntries.fArray[idx+1]=1;
1784 }
1785 else
1786 if (val>fArray[idx+1])
1787 fArray[idx+1] = val;
1788 }
1789 fEntries++;
1790}
1791
1792// ------------------------------------------------------------------------
1793//
1794// Call this function to add a MCamEvent on top of the present contents.
1795// 1 is added to each pixel if the contents of MCamEvent>threshold
1796// (in case isabove is set to kTRUE == default)
1797// 1 is added to each pixel if the contents of MCamEvent<threshold
1798// (in case isabove is set to kFALSE)
1799//
1800// in unused pixel is not counted if it didn't fullfill the condition.
1801//
1802void MHCamera::SetMinCamContent(const MCamEvent &event, Int_t type)
1803{
1804 if (fNcells<=1 || IsFreezed())
1805 return;
1806
1807 // FIXME: Security check missing!
1808 for (Int_t idx=0; idx<fNcells-2; idx++)
1809 {
1810 Double_t val=0;
1811 const Bool_t rc = event.GetPixelContent(val, idx, *fGeomCam, type);
1812 if (!rc)
1813 continue;
1814
1815 if (!IsUsed(idx))
1816 {
1817 fArray[idx+1] = val;
1818 SetUsed(idx);
1819 fBinEntries.fArray[idx+1]=1;
1820 }
1821 else
1822 if (val<fArray[idx+1])
1823 fArray[idx+1] = val;
1824 }
1825 fEntries++;
1826}
1827
1828// ------------------------------------------------------------------------
1829//
1830// Fill the pixels with random contents.
1831//
1832void MHCamera::FillRandom()
1833{
1834 if (fNcells<=1 || IsFreezed())
1835 return;
1836
1837 Reset();
1838
1839 // FIXME: Security check missing!
1840 for (Int_t idx=0; idx<fNcells-2; idx++)
1841 {
1842 Fill(idx, gRandom->Uniform()*fGeomCam->GetPixRatio(idx));
1843 SetUsed(idx);
1844 }
1845 fEntries=1;
1846}
1847
1848
1849// ------------------------------------------------------------------------
1850//
1851// The array must be in increasing order, eg: 2.5, 3.7, 4.9
1852// The values in each bin are replaced by the interval in which the value
1853// fits. In the example we have four intervals
1854// (<2.5, 2.5-3.7, 3.7-4.9, >4.9). Maximum and minimum are set
1855// accordingly.
1856//
1857void MHCamera::SetLevels(const TArrayF &arr)
1858{
1859 if (fNcells<=1)
1860 return;
1861
1862 for (Int_t i=0; i<fNcells-2; i++)
1863 {
1864 if (!IsUsed(i))
1865 continue;
1866
1867 Int_t j = arr.GetSize();
1868 while (j && fArray[i+1]<arr[j-1])
1869 j--;
1870
1871 fArray[i+1] = j;
1872 }
1873 SetMaximum(arr.GetSize());
1874 SetMinimum(0);
1875}
1876
1877// ------------------------------------------------------------------------
1878//
1879// Reset the all pixel colors to a default value
1880//
1881void MHCamera::Reset(Option_t *opt)
1882{
1883 if (fNcells<=1 || IsFreezed())
1884 return;
1885
1886 TH1::Reset(opt);
1887
1888 fUsed.Reset();
1889 fBinEntries.Reset();
1890
1891 for (Int_t i=0; i<fNcells; i++)
1892 fArray[i] = 0;
1893}
1894
1895// ------------------------------------------------------------------------
1896//
1897// Here we calculate the color index for the current value.
1898// The color index is defined with the class TStyle and the
1899// Color palette inside. We use the command gStyle->SetPalette(1,0)
1900// for the display. So we have to convert the value "wert" into
1901// a color index that fits the color palette.
1902// The range of the color palette is defined by the values fMinPhe
1903// and fMaxRange. Between this values we have 50 color index, starting
1904// with 0 up to 49.
1905//
1906Int_t MHCamera::GetColor(Float_t val, Float_t min, Float_t max, Bool_t islog)
1907{
1908 //
1909 // first treat the over- and under-flows
1910 //
1911 const Int_t ncol = GetContour()==0?50:GetContour();
1912
1913 const Int_t maxcolidx = ncol-1;
1914
1915 if (!TMath::Finite(val)) // FIXME: gLog!
1916 return maxcolidx/2;
1917
1918 if (val >= max)
1919 return gStyle->GetColorPalette(maxcolidx);
1920
1921 if (val <= min)
1922 return gStyle->GetColorPalette(0);
1923
1924 //
1925 // calculate the color index
1926 //
1927 Float_t ratio;
1928 if (islog && min>0)
1929 ratio = log10(val/min) / log10(max/min);
1930 else
1931 ratio = (val-min) / (max-min);
1932
1933 const Int_t colidx = TMath::FloorNint(ratio*ncol);
1934 return gStyle->GetColorPalette(colidx);
1935}
1936
1937TPaveStats *MHCamera::GetStatisticBox()
1938{
1939 TObject *obj = 0;
1940
1941 TIter Next(fFunctions);
1942 while ((obj = Next()))
1943 if (obj->InheritsFrom(TPaveStats::Class()))
1944 return static_cast<TPaveStats*>(obj);
1945
1946 return NULL;
1947}
1948
1949// ------------------------------------------------------------------------
1950//
1951// Change the text on the legend according to the range of the Display
1952//
1953void MHCamera::UpdateLegend(Float_t min, Float_t max, Bool_t islog)
1954{
1955 const Float_t range = fGeomCam->GetMaxRadius();
1956
1957 if (!TestBit(kNoScale))
1958 {
1959 TArrow arr;
1960 arr.PaintArrow(-range*.99, -range*.99, -range*.70, -range*.99, 0.010);
1961 arr.PaintArrow(-range*.99, -range*.99, -range*.99, -range*.70, 0.010);
1962
1963 TString text;
1964 text += (int)(range*(0.99-0.70));
1965 text += "mm";
1966
1967 TText newtxt2;
1968 newtxt2.SetTextSize(0.04);
1969 newtxt2.PaintText(-range*.95, -range*.95, text);
1970
1971 text = MString::Format("%.2f#circ", range*(0.99-0.70)*fGeomCam->GetConvMm2Deg());
1972 text = text.Strip(TString::kLeading);
1973
1974 TLatex latex;
1975 latex.PaintLatex(-range*.95, -range*.85, 0, 0.04, text);
1976 }
1977
1978 if (!TestBit(kNoLegend))
1979 {
1980 TPaveStats *stats = GetStatisticBox();
1981
1982 const Float_t hndc = 0.88 - (stats ? stats->GetY1NDC() : 1);
1983 const Float_t H = (0.80-hndc)*range;
1984 const Float_t offset = hndc*range;
1985
1986 const Int_t ncol = GetContour()==0 ? 50 : GetContour();
1987
1988 const Float_t h = 2./ncol;
1989 const Float_t w = range/sqrt((float)(fNcells-2));
1990
1991 TBox newbox;
1992 TText newtxt;
1993 newtxt.SetTextSize(0.03);
1994 newtxt.SetTextAlign(12);
1995#if ROOT_VERSION_CODE > ROOT_VERSION(3,01,06)
1996 newtxt.SetBit(/*kNoContextMenu|*/kCannotPick);
1997 newbox.SetBit(/*kNoContextMenu|*/kCannotPick);
1998#endif
1999
2000 const Float_t step = (islog && min>0 ? log10(max/min) : max-min);
2001 const Int_t firsts = step/48*3 < 1e-8 ? 8 : (Int_t)floor(log10(step/48*3));
2002 const TString opt = MString::Format("%%.%if", firsts>0 ? 0 : TMath::Abs(firsts));
2003/*
2004 for (Int_t i=0; i<ncol+1; i+=3)
2005 {
2006 Float_t val;
2007 if (islog && min>0)
2008 val = pow(10, step*i) * min;
2009 else
2010 val = min + step*i;
2011
2012 //const bool dispexp = max-min>1.5 && fabs(val)>0.1 && fabs(val)<1e6;
2013 newtxt.PaintText(range+1.5*w, H*(i*h-1)-offset, Form(opt, val));
2014 }
2015 */
2016 const MBinning bins(25, min, max, islog&&min>0?"log":"lin");
2017
2018 for (Int_t i=0; i<=25; i++)
2019 newtxt.PaintText(range+1.5*w, H*(i*ncol/25*h-1)-offset, MString::Format(opt, bins[i]));
2020
2021 for (Int_t i=0; i<ncol; i++)
2022 {
2023 newbox.SetFillColor(gStyle->GetColorPalette(i));
2024 newbox.PaintBox(range, H*(i*h-1)-offset, range+w, H*((i+1)*h-1)-offset);
2025 }
2026 }
2027}
2028
2029// ------------------------------------------------------------------------
2030//
2031// Save primitive as a C++ statement(s) on output stream out
2032//
2033void MHCamera::SavePrimitive(ostream &out, Option_t *opt)
2034{
2035 gLog << err << "MHCamera::SavePrimitive: Must be rewritten!" << endl;
2036 /*
2037 if (!gROOT->ClassSaved(TCanvas::Class()))
2038 fDrawingPad->SavePrimitive(out, opt);
2039
2040 out << " " << fDrawingPad->GetName() << "->SetWindowSize(";
2041 out << fDrawingPad->GetWw() << "," << fDrawingPad->GetWh() << ");" << endl;
2042 */
2043}
2044
2045void MHCamera::SavePrimitive(ofstream &out, Option_t *)
2046{
2047 MHCamera::SavePrimitive(static_cast<ostream&>(out), "");
2048}
2049
2050// ------------------------------------------------------------------------
2051//
2052// compute the distance of a point (px,py) to the Camera
2053// this functions needed for graphical primitives, that
2054// means without this function you are not able to interact
2055// with the graphical primitive with the mouse!!!
2056//
2057// All calcutations are done in pixel coordinates
2058//
2059Int_t MHCamera::DistancetoPrimitive(Int_t px, Int_t py)
2060{
2061 if (fNcells<=1)
2062 return 999999;
2063
2064 TPaveStats *box = (TPaveStats*)gPad->GetPrimitive("stats");
2065 if (box)
2066 {
2067 const Double_t w = box->GetY2NDC()-box->GetY1NDC();
2068 box->SetX1NDC(gStyle->GetStatX()-gStyle->GetStatW());
2069 box->SetY1NDC(gStyle->GetStatY()-w);
2070 box->SetX2NDC(gStyle->GetStatX());
2071 box->SetY2NDC(gStyle->GetStatY());
2072 }
2073
2074 if (TString(GetDrawOption()).Contains("hist", TString::kIgnoreCase))
2075 return TH1D::DistancetoPrimitive(px, py);
2076
2077 const Bool_t issame = TString(GetDrawOption()).Contains("same", TString::kIgnoreCase);
2078
2079 const Float_t maxr = (1-fGeomCam->GetConvMm2Deg())*fGeomCam->GetMaxRadius()/2;
2080 const Float_t conv = !issame ||
2081 gPad->GetX1()<-maxr || gPad->GetY1()<-maxr ||
2082 gPad->GetX2()> maxr || gPad->GetY2()>maxr ? 1 : fGeomCam->GetConvMm2Deg();
2083
2084 if (GetPixelIndex(px, py, conv)>=0)
2085 return 0;
2086
2087 if (!box)
2088 return 999999;
2089
2090 const Int_t dist = box->DistancetoPrimitive(px, py);
2091 if (dist > TPad::GetMaxPickDistance())
2092 return 999999;
2093
2094 gPad->SetSelected(box);
2095 return dist;
2096}
2097
2098// ------------------------------------------------------------------------
2099//
2100//
2101Int_t MHCamera::GetPixelIndex(Int_t px, Int_t py, Float_t conv) const
2102{
2103 if (fNcells<=1)
2104 return -1;
2105
2106 for (Int_t i=0; i<fNcells-2; i++)
2107 if ((*fGeomCam)[i].DistancetoPrimitive(TMath::Nint(px*conv), TMath::Nint(py*conv))<=0)
2108 return i;
2109
2110 return -1;
2111}
2112
2113// ------------------------------------------------------------------------
2114//
2115// Returns string containing info about the object at position (px,py).
2116// Returned string will be re-used (lock in MT environment).
2117//
2118char *MHCamera::GetObjectInfo(Int_t px, Int_t py) const
2119{
2120 if (TString(GetDrawOption()).Contains("hist", TString::kIgnoreCase))
2121#if ROOT_VERSION_CODE < ROOT_VERSION(5,22,00)
2122 return MH::GetObjectInfoH(px, py, *this);
2123#else
2124 return TH1D::GetObjectInfo(px, py);
2125#endif
2126
2127 static char info[128];
2128
2129 const Int_t idx=GetPixelIndex(px, py);
2130
2131 if (idx<0)
2132 return TObject::GetObjectInfo(px, py);
2133
2134 sprintf(info, "Software Pixel Idx: %d (Hardware Id=%d) c=%.1f <%s>",
2135 idx, idx+1, GetBinContent(idx+1), IsUsed(idx)?"on":"off");
2136 return info;
2137}
2138
2139// ------------------------------------------------------------------------
2140//
2141// Add a MCamEvent which should be displayed when the user clicks on a
2142// pixel.
2143// Warning: The object MUST inherit from TObject AND MCamEvent
2144//
2145void MHCamera::AddNotify(TObject *obj)
2146{
2147 // Make sure, that the object derives from MCamEvent!
2148 MCamEvent *evt = dynamic_cast<MCamEvent*>(obj);
2149 if (!evt)
2150 {
2151 gLog << err << "ERROR: MHCamera::AddNotify - TObject doesn't inherit from MCamEvent... ignored." << endl;
2152 return;
2153 }
2154
2155 // Make sure, that it is deleted from the list too, if the obj is deleted
2156 obj->SetBit(kMustCleanup);
2157
2158 // Add object to list
2159 fNotify->Add(obj);
2160}
2161
2162// ------------------------------------------------------------------------
2163//
2164// Execute a mouse event on the camera
2165//
2166void MHCamera::ExecuteEvent(Int_t event, Int_t px, Int_t py)
2167{
2168 if (TString(GetDrawOption()).Contains("hist", TString::kIgnoreCase))
2169 {
2170 TH1D::ExecuteEvent(event, px, py);
2171 return;
2172 }
2173 //if (event==kMouseMotion && fStatusBar)
2174 // fStatusBar->SetText(GetObjectInfo(px, py), 0);
2175 if (event!=kButton1Down)
2176 return;
2177
2178 const Int_t idx = GetPixelIndex(px, py);
2179 if (idx<0)
2180 return;
2181
2182 gLog << all << GetTitle() << " <" << GetName() << ">" << dec << endl;
2183 gLog << "Software Pixel Idx: " << idx << endl;
2184 gLog << "Hardware Pixel Id: " << idx+1 << endl;
2185 gLog << "Contents: " << GetBinContent(idx+1);
2186 if (GetBinError(idx+1)>0)
2187 gLog << " +/- " << GetBinError(idx+1);
2188 gLog << " <" << (IsUsed(idx)?"on":"off") << "> n=" << fBinEntries[idx+1] << endl;
2189
2190 if (fNotify && fNotify->GetSize()>0)
2191 {
2192 // FIXME: Is there a simpler and more convinient way?
2193
2194 // The name which is created here depends on the instance of
2195 // MHCamera and on the pad on which it is drawn --> The name
2196 // is unique. For ExecuteEvent gPad is always correctly set.
2197 const TString name = MString::Format("%p;%p;PixelContent", this, gPad);
2198
2199 TCanvas *old = (TCanvas*)gROOT->GetListOfCanvases()->FindObject(name);
2200 if (old)
2201 old->cd();
2202 else
2203 new TCanvas(name);
2204
2205 /*
2206 TIter Next(gPad->GetListOfPrimitives());
2207 TObject *o;
2208 while (o=Next()) cout << o << ": " << o->GetName() << " " << o->IsA()->GetName() << endl;
2209 */
2210
2211 // FIXME: Make sure, that the old histograms are really deleted.
2212 // Are they already deleted?
2213
2214 // The dynamic_cast is necessary here: We cannot use ForEach
2215 TIter Next(fNotify);
2216 MCamEvent *evt;
2217 while ((evt=dynamic_cast<MCamEvent*>(Next())))
2218 evt->DrawPixelContent(idx);
2219
2220 gPad->Modified();
2221 gPad->Update();
2222 }
2223}
2224
2225UInt_t MHCamera::GetNumPixels() const
2226{
2227 return fGeomCam ? fGeomCam->GetNumPixels() : 0;
2228}
2229
2230// --------------------------------------------------------------------------
2231//
2232// Draw a projection of MHCamera onto the y-axis values. Depending on the
2233// variable fit, the following fits are performed:
2234//
2235// 0: No fit, simply draw the projection
2236// 1: Single Gauss (for distributions flat-fielded over the whole camera)
2237// 2: Double Gauss (for distributions different for inner and outer pixels)
2238// 3: Triple Gauss (for distributions with inner, outer pixels and outliers)
2239// 4: flat (for the probability distributions)
2240// (1-4:) Moreover, sectors 6,1 and 2 of the camera and sectors 3,4 and 5 are
2241// drawn separately, for inner and outer pixels.
2242// 5: Fit Inner and Outer pixels separately by a single Gaussian
2243// (only for MAGIC cameras)
2244// 6: Fit Inner and Outer pixels separately by a single Gaussian and display
2245// additionally the two camera halfs separately (for MAGIC camera)
2246// 7: Single Gauss with TLegend to show the meaning of the colours
2247//
2248void MHCamera::DrawProjection(Int_t fit) const
2249{
2250 if (fit==5 || fit==6)
2251 {
2252 const UInt_t n = fGeomCam->GetNumAreas();
2253
2254 TVirtualPad *pad = gPad;
2255 pad->Divide(n, 1, 1e-5, 1e-5);;
2256
2257 for (UInt_t i=0; i<n; i++)
2258 {
2259 pad->cd(i+1);
2260 gPad->SetBorderMode(0);
2261 gPad->SetRightMargin(0.025);
2262 gPad->SetTicks();
2263
2264 TH1D &h = *ProjectionS(TArrayI(), TArrayI(1, (Int_t*)&i),
2265 MString::Format("%s_%d", GetName(), i));
2266 h.SetTitle(MString::Format("%s %d", GetTitle(), i));
2267 h.SetDirectory(NULL);
2268 h.SetBit(kCanDelete);
2269 h.Draw();
2270
2271 TAxis *xaxe = h.GetXaxis();
2272 TAxis *yaxe = h.GetYaxis();
2273
2274 xaxe->CenterTitle();
2275 yaxe->CenterTitle();
2276 xaxe->SetTitleSize(0.06);
2277 yaxe->SetTitleSize(0.06);
2278 xaxe->SetTitleOffset(0.8);
2279 yaxe->SetTitleOffset(0.85);
2280 xaxe->SetLabelSize(0.05);
2281 yaxe->SetLabelSize(0.05);
2282 if (i>0)
2283 yaxe->SetTitle("");
2284
2285 if (fit==5)
2286 continue;
2287
2288 h.Fit("gaus", "Q");
2289
2290 TF1 *f = h.GetFunction("gaus");
2291 if (f)
2292 {
2293 f->SetLineWidth(2);
2294 f->SetLineColor(kBlue);
2295 }
2296 }
2297 return;
2298 }
2299
2300 TH1D *obj2 = (TH1D*)Projection(GetName());
2301 obj2->SetDirectory(0);
2302 obj2->Draw();
2303 obj2->SetBit(kCanDelete);
2304
2305 if (fit==0)
2306 return;
2307
2308 const Double_t xmin = obj2->GetBinCenter(obj2->GetXaxis()->GetFirst());
2309 const Double_t xmax = obj2->GetBinCenter(obj2->GetXaxis()->GetLast());
2310 const Double_t integ = obj2->Integral("width")/2.5;
2311 const Double_t max = obj2->GetMaximum();
2312 const Double_t mean = obj2->GetMean();
2313 const Double_t rms = obj2->GetRMS();
2314 const Double_t width = xmax-xmin;
2315
2316 const char *dgausformula = "([0]-[3])/[2]*exp(-0.5*(x-[1])*(x-[1])/[2]/[2])"
2317 "+[3]/[5]*exp(-0.5*(x-[4])*(x-[4])/[5]/[5])";
2318
2319 const char *tgausformula = "([0]-[3]-[6])/[2]*exp(-0.5*(x-[1])*(x-[1])/[2]/[2])"
2320 "+[3]/[5]*exp(-0.5*(x-[4])*(x-[4])/[5]/[5])"
2321 "+[6]/[8]*exp(-0.5*(x-[7])*(x-[7])/[8]/[8])";
2322
2323 TF1 *f=0;
2324 switch (fit)
2325 {
2326 case 1: // Single gauss
2327 f = new TF1("sgaus", "gaus", xmin, xmax);
2328 f->SetLineColor(kBlue);
2329 f->SetBit(kCanDelete);
2330 f->SetParNames("Max", "#mu", "#sigma");
2331 f->SetParameters(max, mean, rms);
2332 f->SetParLimits(0, 0, max*2);
2333 f->SetParLimits(1, xmin, xmax);
2334 f->SetParLimits(2, 0, width/1.5);
2335
2336 obj2->Fit(f, "QLR");
2337 break;
2338
2339 case 2: // Double gauss
2340 f = new TF1("dgaus", dgausformula, xmin, xmax);
2341 f->SetLineColor(kBlue);
2342 f->SetBit(kCanDelete);
2343 f->SetParNames("A_{tot}", "#mu1", "#sigma1", "A2", "#mu2", "#sigma2");
2344 f->SetParameters(integ, (xmin+mean)/2., width/4.,
2345 integ/width/2., (xmax+mean)/2., width/4.);
2346 // The left-sided Gauss
2347 f->SetParLimits(0, integ-1.5, integ+1.5);
2348 f->SetParLimits(1, xmin+(width/10.), mean);
2349 f->SetParLimits(2, 0, width/2.);
2350 // The right-sided Gauss
2351 f->SetParLimits(3, 0, integ);
2352 f->SetParLimits(4, mean, xmax-(width/10.));
2353 f->SetParLimits(5, 0, width/2.);
2354 obj2->Fit(f,"QLRM");
2355 break;
2356
2357 case 3: // Triple gauss
2358 f = new TF1("tgaus", tgausformula, xmin,xmax);
2359 f->SetLineColor(kBlue);
2360 f->SetBit(kCanDelete);
2361 f->SetParNames("A_{tot}","#mu_{1}","#sigma_{1}",
2362 "A_{2}","#mu_{2}","#sigma_{2}",
2363 "A_{3}","#mu_{3}","#sigma_{3}");
2364 f->SetParameters(integ, (xmin+mean)/2, width/4.,
2365 integ/width/3., (xmax+mean)/2., width/4.,
2366 integ/width/3., mean,width/2.);
2367 // The left-sided Gauss
2368 f->SetParLimits(0, integ-1.5, integ+1.5);
2369 f->SetParLimits(1, xmin+(width/10.), mean);
2370 f->SetParLimits(2, width/15., width/2.);
2371 // The right-sided Gauss
2372 f->SetParLimits(3, 0., integ);
2373 f->SetParLimits(4, mean, xmax-(width/10.));
2374 f->SetParLimits(5, width/15., width/2.);
2375 // The Gauss describing the outliers
2376 f->SetParLimits(6, 0., integ);
2377 f->SetParLimits(7, xmin, xmax);
2378 f->SetParLimits(8, width/4., width/1.5);
2379 obj2->Fit(f,"QLRM");
2380 break;
2381
2382 case 4:
2383 obj2->Fit("pol0", "Q");
2384 if (obj2->GetFunction("pol0"))
2385 obj2->GetFunction("pol0")->SetLineColor(kBlue);
2386 break;
2387
2388 case 9:
2389 break;
2390
2391 default:
2392 obj2->Fit("gaus", "Q");
2393 if (obj2->GetFunction("gaus"))
2394 obj2->GetFunction("gaus")->SetLineColor(kBlue);
2395 break;
2396 }
2397}
2398
2399// --------------------------------------------------------------------------
2400//
2401// Draw a projection of MHCamera vs. the radius from the central pixel.
2402//
2403// The inner and outer pixels are drawn separately, both fitted by a polynomial
2404// of grade 1.
2405//
2406void MHCamera::DrawRadialProfile() const
2407{
2408 TProfile *obj2 = (TProfile*)RadialProfile(GetName());
2409 obj2->SetDirectory(0);
2410 obj2->Draw();
2411 obj2->SetBit(kCanDelete);
2412/*
2413 if (GetGeomCam().InheritsFrom("MGeomCamMagic"))
2414 {
2415 TArrayI s0(6);
2416 s0[0] = 1;
2417 s0[1] = 2;
2418 s0[2] = 3;
2419 s0[3] = 4;
2420 s0[4] = 5;
2421 s0[5] = 6;
2422
2423 TArrayI inner(1);
2424 inner[0] = 0;
2425
2426 TArrayI outer(1);
2427 outer[0] = 1;
2428
2429 // Just to get the right (maximum) binning
2430 TProfile *half[2];
2431 half[0] = RadialProfileS(s0, inner, MString::Format("%sInner",GetName()));
2432 half[1] = RadialProfileS(s0, outer, MString::Format("%sOuter",GetName()));
2433
2434 for (Int_t i=0; i<2; i++)
2435 {
2436 Double_t min = GetGeomCam().GetMinRadius(i);
2437 Double_t max = GetGeomCam().GetMaxRadius(i);
2438
2439 half[i]->SetLineColor(kRed+i);
2440 half[i]->SetDirectory(0);
2441 half[i]->SetBit(kCanDelete);
2442 half[i]->Draw("same");
2443 half[i]->Fit("pol1","Q","",min,max);
2444 half[i]->GetFunction("pol1")->SetLineColor(kRed+i);
2445 half[i]->GetFunction("pol1")->SetLineWidth(1);
2446 }
2447 }
2448*/
2449}
2450
2451// --------------------------------------------------------------------------
2452//
2453// Draw a projection of MHCamera vs. the azimuth angle inside the camera.
2454//
2455// The inner and outer pixels are drawn separately.
2456// The general azimuth profile is fitted by a straight line
2457//
2458void MHCamera::DrawAzimuthProfile() const
2459{
2460 TProfile *obj2 = (TProfile*)AzimuthProfile(GetName());
2461 obj2->SetDirectory(0);
2462 obj2->Draw();
2463 obj2->SetBit(kCanDelete);
2464 obj2->Fit("pol0","Q","");
2465 if (obj2->GetFunction("pol0"))
2466 obj2->GetFunction("pol0")->SetLineWidth(1);
2467/*
2468 if (GetGeomCam().InheritsFrom("MGeomCamMagic"))
2469 {
2470 TArrayI inner(1);
2471 inner[0] = 0;
2472
2473 TArrayI outer(1);
2474 outer[0] = 1;
2475
2476 // Just to get the right (maximum) binning
2477 TProfile *half[2];
2478 half[0] = AzimuthProfileA(inner, MString::Format("%sInner",GetName()));
2479 half[1] = AzimuthProfileA(outer, MString::Format("%sOuter",GetName()));
2480
2481 for (Int_t i=0; i<2; i++)
2482 {
2483 half[i]->SetLineColor(kRed+i);
2484 half[i]->SetDirectory(0);
2485 half[i]->SetBit(kCanDelete);
2486 half[i]->SetMarkerSize(0.5);
2487 half[i]->Draw("same");
2488 }
2489 }
2490*/
2491}
2492
2493// --------------------------------------------------------------------------
2494//
2495// Draw the MHCamera into the MStatusDisplay:
2496//
2497// 1) Draw it as histogram (MHCamera::DrawCopy("hist")
2498// 2) Draw it as a camera, with MHCamera::SetPrettyPalette() set.
2499// 3) If "rad" is not zero, draw its values vs. the radius from the camera center.
2500// (DrawRadialProfile())
2501// 4) Depending on the variable "fit", draw the values projection on the y-axis
2502// (DrawProjection()):
2503// 0: don't draw
2504// 1: Draw fit to Single Gauss (for distributions flat-fielded over the whole camera)
2505// 2: Draw and fit to Double Gauss (for distributions different for inner and outer pixels)
2506// 3: Draw and fit to Triple Gauss (for distributions with inner, outer pixels and outliers)
2507// 4: Draw and fit to Polynomial grade 0: (for the probability distributions)
2508// >4: Draw and don;t fit.
2509//
2510void MHCamera::CamDraw(TCanvas &c, const Int_t x, const Int_t y,
2511 const Int_t fit, const Int_t rad, const Int_t azi,
2512 TObject *notify)
2513{
2514 c.cd(x);
2515 gPad->SetBorderMode(0);
2516 gPad->SetRightMargin(0.02);
2517 gPad->SetTicks();
2518 MHCamera *obj1=(MHCamera*)DrawCopy("hist");
2519 obj1->SetDirectory(NULL);
2520 obj1->SetStats(kFALSE);
2521
2522 if (notify)
2523 obj1->AddNotify(notify);
2524
2525 c.cd(x+y);
2526 gPad->SetBorderMode(0);
2527 obj1->SetPrettyPalette();
2528 obj1->Draw();
2529
2530 Int_t cnt = 2;
2531
2532 if (rad)
2533 {
2534 c.cd(x+2*y);
2535 gPad->SetBorderMode(0);
2536 gPad->SetTicks();
2537 DrawRadialProfile();
2538 cnt++;
2539 }
2540
2541 if (azi)
2542 {
2543 c.cd(x+cnt*y);
2544 gPad->SetBorderMode(0);
2545 gPad->SetTicks();
2546 DrawAzimuthProfile();
2547 cnt++;
2548 }
2549
2550 if (fit<0)
2551 return;
2552
2553 c.cd(x + cnt*y);
2554 gPad->SetBorderMode(0);
2555 gPad->SetRightMargin(0.025);
2556 gPad->SetTicks();
2557 DrawProjection(fit);
2558}
Note: See TracBrowser for help on using the repository browser.