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

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