<commit_before>// @(#)root/graf:$Name:  $:$Id: TPie.cxx,v 1.3 2006/11/22 15:42:22 couet Exp $
// Author: <NAME>, <NAME> 03/11/2006

/*************************************************************************
 * Copyright (C) 1995-2000, <NAME> and <NAME>.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

#include "TPie.h"

#include <Riostream.h>
#include <TROOT.h>
#include <TPad.h>
#include <TArc.h>
#include <TMath.h>
#include <TStyle.h>
#include <TLatex.h>
#include <TPaveText.h>
#include <TH1.h>

ClassImp(TPie)

//______________________________________________________________________________
//
// Draw a Pie Chart
//
// The macro $ROOTSYS/tutorials/piechart.C produces the following picture:
//Begin_Html
/*
<img src="gif/piechart.gif">
*/
//End_Html
//

static Double_t gX             = 0; // temporary pie X position
static Double_t gY             = 0; // temporary pie Y position
static Double_t gRadius        = 0; // temporary pie Radius of the TPie
static Double_t gRadiusOffset  = 0; // temporary slice's radial offset
static Double_t gAngularOffset = 0; // temporary slice's angular offset
static Bool_t   gIsUpt         = kFALSE; // True is the TPie should be updated


//______________________________________________________________________________
TPie::TPie() : TNamed()
{
   // Default constructor.

   Init(1, 0, 0.5, 0.5, 0.4);
}


//______________________________________________________________________________
TPie::TPie(const char *name, const char *title, Int_t npoints) :
   TNamed(name,title)
{
   // This constructor creates a pie chart when only the number of
   // the slices is known. The number of slices is fixed.

   Init(npoints, 0, 0.5, 0.5, 0.4);
}


//______________________________________________________________________________
TPie::TPie(const char *name,
               const char *title,
               Int_t npoints, Double_t *vals,
               Int_t *colors, const char *lbls[]) : TNamed(name,title)
{
   // Normal constructor. The 1st and 2nd parameters are the name of the object
   // and its title.
   //
   // The number of points passed at this point is used to allocate the memory.
   //
   // Slices values are given as Double_t.
   //
   // The 4th elements is an array containing, in double precision format,
   // the value of each slice. It is also possible to specify the filled color
   // of each slice. If the color array is not specfied the slices are colored
   // using a color sequence in the standard palette.

   Init(npoints, 0, 0.5, 0.5, 0.4);
   for (Int_t i=0; i<fNvals; ++i) fVals[i] = vals[i];

   SetFillColors(colors);
   SetLabels(lbls);
}


//______________________________________________________________________________
TPie::TPie(const char *name,
               const char *title,
               Int_t npoints, Float_t *vals,
               Int_t *colors, const char *lbls[]) : TNamed(name,title)
{
   // Normal constructor (Float_t).

   Init(npoints, 0, 0.5, 0.5, 0.4);
   for (Int_t i=0; i<fNvals; ++i) fVals[i] = vals[i];

   SetFillColors(colors);
   SetLabels(lbls);
}


//______________________________________________________________________________
TPie::TPie(const TH1 *h) : TNamed(h->GetName(),h->GetTitle())
{
   // Constructor from a TH1

   Init(h->GetNbinsX(), 0, 0.5, 0.5, 0.4);
   for (Int_t i=0; i<fNvals; ++i) fVals[i] = h->GetBin(i);
}


//______________________________________________________________________________
TPie::TPie(const TPie &cpy) : TNamed(cpy), TAttText(cpy)
{
   // Copy constructor.

   Init(cpy.fNvals, cpy.fAngularOffset, cpy.fX, cpy.fY, cpy.fRadius);

   for (Int_t i=0;i<fNvals;++i) {
      fVals[i]          = cpy.fVals[i];
      fLineColors[i]    = cpy.fLineColors[i];
      fLineStyles[i]    = cpy.fLineStyles[i];
      fLineWidths[i]    = cpy.fLineWidths[i];
      fFillColors[i]    = cpy.fFillColors[i];
      fFillStyles[i]    = cpy.fFillStyles[i];
      fRadiusOffsets[i] = cpy.fRadiusOffsets[i];
   }
}

//______________________________________________________________________________
TPie::~TPie()
{
   // Destructor.

   if (fNvals>0) {
      delete [] fVals;
      delete [] fLabels;
      delete [] fLineColors;
      delete [] fLineStyles;
      delete [] fLineWidths;
      delete [] fFillColors;
      delete [] fFillStyles;
      delete [] fRadiusOffsets;
   }

   if (fSlices) delete [] fSlices;
}


//______________________________________________________________________________
Int_t TPie::DistancetoPrimitive(Int_t px, Int_t py)
{
   // Evaluate the distance to the chart in gPad.

   if ( (fCurrent_slice = DistancetoSlice(px,py))>=0 ) {
      if (fCurrent_rad<=fRadius) {
         return 0;
      } else {
         Int_t tmpx = gPad->XtoPixel((fCurrent_rad-fRadius)*
                                     TMath::Cos(fCurrent_ang));
         Int_t tmpy = gPad->YtoPixel((fCurrent_rad-fRadius)*
                                     TMath::Sin(fCurrent_ang));
         return (Int_t) TMath::Sqrt(tmpx*tmpx+tmpy*tmpy);
      }
   } else {
      return 9999;
   }
}


//______________________________________________________________________________
Int_t TPie::DistancetoSlice(Int_t px, Int_t py)
{
   // Returns the slice number at the pixel position (px,py).
   // Returns -1 if no slice is picked.
   //
   // Used by DistancetoPrimitive.

   MakeSlices();

   Int_t result(-1);

   // coordinates
   Double_t xx = gPad->PadtoX(gPad->AbsPixeltoX(px));
   Double_t yy = gPad->PadtoY(gPad->AbsPixeltoY(py));

   Double_t phimin;
   Double_t cphi;
   Double_t phimax;
   for (Int_t i=0;i<fNvals;++i) {
      if (gIsUpt && fCurrent_slice!=i) continue;

      // Angles' values for this slice
      phimin = fSlices[2*i  ]*TMath::Pi()/180.;
      cphi   = fSlices[2*i+1]*TMath::Pi()/180.;
      phimax = fSlices[2*i+2]*TMath::Pi()/180.;

      // Recalculate slice's vertex' coordinates.
      Double_t x, y, rad, radOffset;
      if (gIsUpt) {
         x         = gX;
         y         = gY;
         rad       = gRadius,
         radOffset = gRadiusOffset;
      }
      else {
         x         = fX;
         y         = fY;
         rad       = fRadius;
         radOffset = fRadiusOffsets[i];
      }

      Double_t dx  = xx-x-radOffset*TMath::Cos(cphi);
      Double_t dy  = yy-y-radOffset*TMath::Sin(cphi);
      Double_t ang = TMath::ATan2(dy,dx);
      if (ang<0) ang += TMath::TwoPi();

      Double_t dist = TMath::Sqrt(dx*dx+dy*dy);

      if ( ((ang>=phimin && ang <= phimax) || (phimax>TMath::TwoPi() &&
             ang+TMath::TwoPi()>=phimin && ang+TMath::TwoPi()<phimax)) &&
             dist<=rad) { // if true the pointer is in the slice region
         fCurrent_x    = dx;
         fCurrent_y    = dy;
         fCurrent_ang  = ang;
         fCurrent_phi1 = phimin;
         fCurrent_phi2 = phimax;
         fCurrent_rad  = dist;
         result        = i;
         break;
      }
   }
   return result;
}


//______________________________________________________________________________
void TPie::Draw(Option_t *option)
{
   // Draw the pie chart.
   //
   // The possible options are listed in the TPie::Paint() method.

   TString soption(option);
   soption.ToLower();

   if (soption.Length()==0) soption = "l";

   if (gPad) {
      if (!gPad->IsEditable()) (gROOT->GetMakeDefCanvas())();
      if (!soption.Contains("same")) {
         gPad->Clear();
         gPad->Range(0.,0.,1.,1.);
      }
   }

   AppendPad(option);
}


//______________________________________________________________________________
void TPie::DrawGhost()
{
   // This method is for internal use. It is used by Execute event to draw the
   // outline of "this" TPie. Used when the opaque movements are not permitted.

   MakeSlices();

   // Loop over slices
   for (Int_t i=0;i<fNvals;++i) {
      Float_t minphi = (fSlices[i*2]+gAngularOffset+.5)*TMath::Pi()/180.;
      Float_t avgphi = fSlices[i*2+1]*TMath::Pi()/180.;
      Float_t maxphi = (fSlices[i*2+2]-.5)*TMath::Pi()/180;

      Double_t radOffset = (i == fCurrent_slice ? gRadiusOffset : fRadiusOffsets[i]);
      Double_t x0 = gX+radOffset*TMath::Cos(avgphi);
      Double_t y0 = gY+radOffset*TMath::Sin(avgphi);

      gVirtualX->DrawLine( gPad->XtoAbsPixel(x0), gPad->YtoAbsPixel(y0),
                           gPad->XtoAbsPixel(x0+gRadius*TMath::Cos(minphi)),
                           gPad->YtoAbsPixel(y0+gRadius*TMath::Sin(minphi)) );

      Int_t ndiv = 10;
      Double_t dphi = (maxphi-minphi)/ndiv;

      if (dphi>.15) ndiv = (Int_t) ((maxphi-minphi)/.15);
      dphi = (maxphi-minphi)/ndiv;

      // Loop to draw the arc
      for (Int_t j=1;j<ndiv;++j) {
         Double_t phi = minphi+dphi*j;
         gVirtualX->DrawLine( gPad->XtoAbsPixel(x0+gRadius*TMath::Cos(phi)),
                              gPad->YtoAbsPixel(y0+gRadius*TMath::Sin(phi)),
                              gPad->XtoAbsPixel(x0+gRadius*TMath::Cos(phi+dphi)),
                              gPad->YtoAbsPixel(y0+gRadius*TMath::Sin(phi+dphi)));
      }

      gVirtualX->DrawLine( gPad->XtoAbsPixel(x0+gRadius*TMath::Cos(maxphi)),
                           gPad->YtoAbsPixel(y0+gRadius*TMath::Sin(maxphi)),
                           gPad->XtoAbsPixel(x0), gPad->YtoAbsPixel(y0) );
   }
}


//______________________________________________________________________________
void TPie::ExecuteEvent(Int_t event, Int_t px, Int_t py)
{
   // Execute the mouse events.

   if (!gPad) return;
   if (!gPad->IsEditable() && event != kMouseEnter) return;

   MakeSlices();

   static bool isMovingPie(kFALSE);
   static bool isMovingSlice(kFALSE);
   static bool isResizing(kFALSE);
   static bool isRotating(kFALSE);
   static bool onBorder(kFALSE);
   bool isRedrawing(kFALSE);
   static Int_t prev_event(-1);
   static Int_t oldpx, oldpy;

   // Portion of pie considered as "border"
   static Double_t dr     = gPad->PixeltoX(3);
   static Double_t minRad = gPad->PixeltoX(10);

   // Angular divisions in radial direction
   static Double_t angstep1 = 0.5*TMath::PiOver4();
   static Double_t angstep2 = 1.5*TMath::PiOver4();
   static Double_t angstep3 = 2.5*TMath::PiOver4();
   static Double_t angstep4 = 3.5*TMath::PiOver4();
   static Double_t angstep5 = 4.5*TMath::PiOver4();
   static Double_t angstep6 = 5.5*TMath::PiOver4();
   static Double_t angstep7 = 6.5*TMath::PiOver4();
   static Double_t angstep8 = 7.5*TMath::PiOver4();

   Int_t dx, dy;
   Double_t mdx, mdy;

   switch(event) {
      case kButton1Down:
         // Change cursor to show pie's movement.
         gVirtualX->SetLineColor(1);
         gVirtualX->SetLineWidth(2);

         // Current center and radius.
         gX             = fX;
         gY             = fY;
         gRadius        = fRadius;
         gRadiusOffset  = fRadiusOffsets[fCurrent_slice];
         gAngularOffset = 0;
         gIsUpt         = kTRUE;

         prev_event = kButton1Down;

      case kMouseMotion:
         if (fCurrent_rad>=fRadius-2.*dr && fCurrent_rad<=fRadius+dr
               && !isMovingPie && !isMovingSlice && !isResizing) {
            if (fCurrent_ang>=angstep8 || fCurrent_ang<angstep1)
               gPad->SetCursor(kRightSide);
            else if (fCurrent_ang>=angstep1 && fCurrent_ang<angstep2)
               gPad->SetCursor(kTopRight);
            else if (fCurrent_ang>=angstep2 && fCurrent_ang<angstep3)
               gPad->SetCursor(kTopSide);
            else if (fCurrent_ang>=angstep3 && fCurrent_ang<angstep4)
               gPad->SetCursor(kTopLeft);
            else if (fCurrent_ang>=angstep4 && fCurrent_ang<=angstep5)
               gPad->SetCursor(kLeftSide);
            else if (fCurrent_ang>=angstep5 && fCurrent_ang<angstep6)
               gPad->SetCursor(kBottomLeft);
            else if (fCurrent_ang>=angstep6 && fCurrent_ang<angstep7)
               gPad->SetCursor(kBottomSide);
            else if (fCurrent_ang>=angstep7 && fCurrent_ang<angstep8)
               gPad->SetCursor(kBottomRight);
            onBorder = kTRUE;
         } else {
            onBorder = kFALSE;
            if (fCurrent_rad>fRadius*.6) {
               gPad->SetCursor(kPointer);
            } else if (fCurrent_rad<=fRadius*.3) {
               gPad->SetCursor(kHand);
            } else if (fCurrent_rad<=fRadius*.6 && fCurrent_rad>=fRadius*.3) {
               gPad->SetCursor(kRotate);
            }
         }
         oldpx = px;
         oldpy = py;
         if (isMovingPie || isMovingSlice) gPad->SetCursor(kMove);
         break;

      case kButton1Motion:
         if (!isMovingSlice || !isMovingPie || !isResizing) {
            if (prev_event==kButton1Down) {
               if (onBorder) {
                  isResizing = kTRUE;
               } else if (fCurrent_rad>=fRadius*.6 && fCurrent_slice>=0) {
                  isMovingSlice = kTRUE;
               } else if (fCurrent_rad<=fRadius*.3) {
                  isMovingPie = kTRUE;
               } else if (fCurrent_rad<fRadius*.6 && fCurrent_rad>fRadius*.3) {
                  isRotating = kTRUE;
               }
            }
         }

         dx = px-oldpx;
         dy = py-oldpy;

         mdx = gPad->PixeltoX(dx);
         mdy = gPad->PixeltoY(dy);

         if (isMovingPie || isMovingSlice) {
            gPad->SetCursor(kMove);
            if (isMovingSlice) {
               Float_t avgphi = fSlices[fCurrent_slice*2+1]*TMath::Pi()/180.;

               if (!gPad->OpaqueMoving()) DrawGhost();

               gRadiusOffset += TMath::Cos(avgphi)*mdx +TMath::Sin(avgphi)*mdy;
               if (gRadiusOffset<0) gRadiusOffset = .0;

               if (!gPad->OpaqueMoving()) DrawGhost();
            } else {
               if (!gPad->OpaqueMoving()) DrawGhost();

               gX += mdx;
               gY += mdy;

               if (!gPad->OpaqueMoving()) DrawGhost();
            }
         } else if (isResizing) {
            if (!gPad->OpaqueResizing()) DrawGhost();

            Float_t dr = mdx*TMath::Cos(fCurrent_ang)+mdy*TMath::Sin(fCurrent_ang);
            if (gRadius+dr>=minRad) {
               gRadius += dr;
            } else {
               gRadius = minRad;
            }

            if (!gPad->OpaqueResizing()) DrawGhost();
         } else if (isRotating) {
            if (!gPad->OpaqueMoving()) DrawGhost();

            if (py>=gPad->YtoAbsPixel(fY)) {
               if (px<oldpx) gAngularOffset--;
               else          gAngularOffset++;
            } else {
               if (px<oldpx) gAngularOffset++;
               else          gAngularOffset--;
            }
            if (gAngularOffset>=360) gAngularOffset = 0;
            if (!gPad->OpaqueMoving()) DrawGhost();
         }

         oldpx = px;
         oldpy = py;
         if ( ((isMovingPie || isMovingSlice) && gPad->OpaqueMoving()) ||
               (isResizing && gPad->OpaqueResizing()) ||
               (isRotating && gPad->OpaqueMoving()) ) isRedrawing = kTRUE;
         else break;

      case kButton1Up:
         if (!isRedrawing) {
            prev_event = kButton1Up;
            gIsUpt = kFALSE;
         }

         if (gROOT->IsEscaped()) {
            gROOT->SetEscape(kFALSE);
            gIsUpt = kFALSE;
            break;
         }

         fX      = gX;
         fY      = gY;
         fRadius = gRadius;
         fRadiusOffsets[fCurrent_slice] = gRadiusOffset;
         SetAngularOffset(fAngularOffset+gAngularOffset);

         gPad->Modified(kTRUE);

         gVirtualX->SetLineColor(-1);
         gVirtualX->SetLineWidth(-1);

         if (isMovingPie)   isMovingPie   = kFALSE;
         if (isMovingSlice) isMovingSlice = kFALSE;
         if (isResizing)    isResizing    = kFALSE;
         if (isRotating)    isResizing    = kFALSE;

         gIsUpt = kFALSE;
         break;

      case kMouseEnter:
         break;

      case kButton1Locate:
         break;

      default:
         // unknown event
         break;
   }
}


//______________________________________________________________________________
const char* TPie::GetEntryLabel(Int_t i)
{
   // Returns the label of the entry number "i".

   return fLabels[i].Data();
}


//______________________________________________________________________________
Int_t TPie::GetEntryFillColor(Int_t i)
{
   // Return the color of the slice number "i".

   return fFillColors[i];
}


//______________________________________________________________________________
Int_t TPie::GetEntryFillStyle(Int_t i)
{
   // Return the style use to fill the slice number "i".

   return fFillStyles[i];
}


//______________________________________________________________________________
Double_t TPie::GetEntryRadiusOffset(Int_t i)
{
   // Return the radial offset's value for the slice number "i".

   return fRadiusOffsets[i];
}


//______________________________________________________________________________
Double_t TPie::GetEntryVal(Int_t i)
{
   // Return the value associated with the slice number "i".

   return fVals[i];
}


//______________________________________________________________________________
void TPie::Init(Int_t np, Double_t ao, Double_t x, Double_t y, Double_t r)
{
   // Common initialization for all constructors.
   // This is a private function called to allocate the memory.

   gIsUpt = kFALSE;

   fAngularOffset = ao;
   fX             = x;
   fY             = y;
   fRadius        = r;
   fNvals         = np;
   fSum           = 0.;
   fSlices        = 0;

   fLabelsOffset = gStyle->GetLabelOffset();

   fLineColors = new Int_t[fNvals];
   fLineStyles = new Int_t[fNvals];
   fLineWidths = new Int_t[fNvals];

   fFillColors = new Int_t[fNvals];
   fFillStyles = new Int_t[fNvals];

   fLabels        = new TString[fNvals];
   fRadiusOffsets = new Double_t[fNvals];
   fVals          = new Double_t[fNvals];

   for (Int_t i=0;i<fNvals;++i) {
      fVals[i]          = 0.;
      fLabels[i]        = "Slice";
      fLabels[i]       += i;
      fLineColors[i]    = 1;
      fLineStyles[i]    = 1;
      fLineWidths[i]    = 1;
      fFillColors[i]    = gStyle->GetColorPalette(i);
      fFillStyles[i]    = 1001;
      fRadiusOffsets[i] = 0.;
   }

   fLabelFormat    = "%txt";
   fFractionFormat = "%3.2f";
   fValFormat      = "%4.2f";
   fPercentFormat  = "%3.1f";
}


//______________________________________________________________________________
void TPie::Paint(Option_t *option)
{
   // Paint a Pie chart in a canvas.
   // The possible option are:
   //
   // "R"  Print the labels along the central "R"adius of slices.
   // "T"  Print the label in a direction "T"angent to circle that describes
   //      the TPie.
   //
   // Other options changing the labels' format are described in
   // TPie::SetLabelFormat().

   MakeSlices();

   TString soption(option);

   bool optionSame(kFALSE);

   // For the label orientation there are 3 possibilities:
   //   0: horizontal
   //   1: radial
   //   2: tangent
   Int_t lblor(0);

   // Parse the options
   Int_t idx;
   // Paint the TPie in an existing canvas
   if ( (idx=soption.Index("same"))>=0 ) {
      optionSame = kTRUE;
      soption.Remove(idx,4);
   }

   // Seek if have to draw the labels around the pie chart
   if ( (idx=soption.Index("t"))>=0 ) {
      lblor = 2;
      soption.Remove(idx,1);
   }

   // Seek if have to paint the labels along the radii
   if ( (idx=soption.Index("r"))>=0 ) {
      lblor = 1;
      soption.Remove(idx,1);
   }

   if (fNvals<=0) {
      Warning("Paint","No vals");
      return;
   }

   if (!fVals) {
      Warning("TPie","No valid arrays of values");
      return;
   }

   // Objects useful to draw labels and slices
   TLatex *textlabel = new TLatex();
   TArc *arc = new TArc();

   // Draw the slices.
   for (Int_t i=0;i<fNvals;++i) {
      // Set the color of the next slice
      arc->SetFillColor(fFillColors[i]);
      arc->SetFillStyle(fFillStyles[i]);
      arc->SetLineColor(fLineColors[i]);
      arc->SetLineStyle(fLineStyles[i]);
      arc->SetLineWidth(fLineWidths[i]);

      // Paint the slice
      Float_t aphi = fSlices[2*i+1]*TMath::Pi()/180.;
      Double_t ax = fX+TMath::Cos(aphi)*fRadiusOffsets[i];
      Double_t ay = fY+TMath::Sin(aphi)*fRadiusOffsets[i];
      arc->PaintEllipse(ax, ay, fRadius, fRadius, fSlices[2*i],
                                                  fSlices[2*i+2], 0.);
   }


   // Loop to place the labels.
   for (Int_t i=0;i<fNvals;++i) {
      Float_t aphi = fSlices[2*i+1]*TMath::Pi()/180.;
      Float_t label_off = fLabelsOffset;

      // Set the font
      textlabel->SetTextFont(GetTextFont());
      textlabel->SetTextSize(GetTextSize());
      textlabel->SetTextColor(GetTextColor());

      textlabel->SetTextAngle(GetTextAngle());

      // Paint the text in the pad
      TString tmptxt  = fLabelFormat;

      tmptxt.ReplaceAll("%txt",fLabels[i]);
      tmptxt.ReplaceAll("%val",Form(fValFormat.Data(),fVals[i]));
      tmptxt.ReplaceAll("%frac",Form(fFractionFormat.Data(),fVals[i]/fSum));
      tmptxt.ReplaceAll("%perc",Form("%3.1f %s",(fVals[i]/fSum)*100,"%"));

      textlabel->SetTitle(tmptxt.Data());
      Double_t h = textlabel->GetYsize();
      Double_t w = textlabel->GetXsize();

      Float_t lx = fX+(fRadius+fRadiusOffsets[i]+label_off)*TMath::Cos(aphi);
      Float_t ly = fY+(fRadius+fRadiusOffsets[i]+label_off)*TMath::Sin(aphi);

      Double_t lblang = GetTextAngle();

      if (lblor==1) {
         lblang += aphi;

         lx += h/2.*TMath::Sin(lblang);
         ly -= h/2.*TMath::Cos(lblang);

         // This control prevent text up-side
         if (aphi>TMath::PiOver2() && aphi<=3.*TMath::PiOver2()) {
            lx     += w*TMath::Cos(lblang)-h*TMath::Sin(lblang);
            ly     += w*TMath::Sin(lblang)+h*TMath::Cos(lblang);
            lblang -= TMath::Pi();
         }
      } else if (lblor==2) {
         lblang += aphi-TMath::PiOver2();
         if (lblang<0) lblang+=TMath::TwoPi();
<commit_msg>- Fix the contructor from a TH1:   * The bins contents were not retrieve properly.   * Take into account the histogram subrange. - Better use of the text angle for slices labels.<commit_after>// @(#)root/graf:$Name:  $:$Id: TPie.cxx,v 1.4 2006/11/23 07:39:15 brun Exp $
// Author: <NAME>, <NAME> 03/11/2006

/*************************************************************************
 * Copyright (C) 1995-2000, <NAME> and <NAME>.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

#include "TPie.h"

#include <Riostream.h>
#include <TROOT.h>
#include <TPad.h>
#include <TArc.h>
#include <TMath.h>
#include <TStyle.h>
#include <TLatex.h>
#include <TPaveText.h>
#include <TH1.h>

ClassImp(TPie)

//______________________________________________________________________________
//
// Draw a Pie Chart
//
// The macro $ROOTSYS/tutorials/piechart.C produces the following picture:
//Begin_Html
/*
<img src="gif/piechart.gif">
*/
//End_Html
//

static Double_t gX             = 0; // temporary pie X position
static Double_t gY             = 0; // temporary pie Y position
static Double_t gRadius        = 0; // temporary pie Radius of the TPie
static Double_t gRadiusOffset  = 0; // temporary slice's radial offset
static Double_t gAngularOffset = 0; // temporary slice's angular offset
static Bool_t   gIsUpt         = kFALSE; // True is the TPie should be updated


//______________________________________________________________________________
TPie::TPie() : TNamed()
{
   // Default constructor.

   Init(1, 0, 0.5, 0.5, 0.4);
}


//______________________________________________________________________________
TPie::TPie(const char *name, const char *title, Int_t npoints) :
   TNamed(name,title)
{
   // This constructor creates a pie chart when only the number of
   // the slices is known. The number of slices is fixed.

   Init(npoints, 0, 0.5, 0.5, 0.4);
}


//______________________________________________________________________________
TPie::TPie(const char *name,
               const char *title,
               Int_t npoints, Double_t *vals,
               Int_t *colors, const char *lbls[]) : TNamed(name,title)
{
   // Normal constructor. The 1st and 2nd parameters are the name of the object
   // and its title.
   //
   // The number of points passed at this point is used to allocate the memory.
   //
   // Slices values are given as Double_t.
   //
   // The 4th elements is an array containing, in double precision format,
   // the value of each slice. It is also possible to specify the filled color
   // of each slice. If the color array is not specfied the slices are colored
   // using a color sequence in the standard palette.

   Init(npoints, 0, 0.5, 0.5, 0.4);
   for (Int_t i=0; i<fNvals; ++i) fVals[i] = vals[i];

   SetFillColors(colors);
   SetLabels(lbls);
}


//______________________________________________________________________________
TPie::TPie(const char *name,
               const char *title,
               Int_t npoints, Float_t *vals,
               Int_t *colors, const char *lbls[]) : TNamed(name,title)
{
   // Normal constructor (Float_t).

   Init(npoints, 0, 0.5, 0.5, 0.4);
   for (Int_t i=0; i<fNvals; ++i) fVals[i] = vals[i];

   SetFillColors(colors);
   SetLabels(lbls);
}


//______________________________________________________________________________
TPie::TPie(const TH1 *h) : TNamed(h->GetName(),h->GetTitle())
{
   // Constructor from a TH1

   Int_t first = h->GetXaxis()->GetFirst();
   Int_t last  = h->GetXaxis()->GetLast();
   Int_t np    = last-first+1;
   Init(np, 0, 0.5, 0.5, 0.4);
   for (Int_t i=first; i<=last; ++i) fVals[i-first] = h->GetBinContent(i);
}


//______________________________________________________________________________
TPie::TPie(const TPie &cpy) : TNamed(cpy), TAttText(cpy)
{
   // Copy constructor.

   Init(cpy.fNvals, cpy.fAngularOffset, cpy.fX, cpy.fY, cpy.fRadius);

   for (Int_t i=0;i<fNvals;++i) {
      fVals[i]          = cpy.fVals[i];
      fLineColors[i]    = cpy.fLineColors[i];
      fLineStyles[i]    = cpy.fLineStyles[i];
      fLineWidths[i]    = cpy.fLineWidths[i];
      fFillColors[i]    = cpy.fFillColors[i];
      fFillStyles[i]    = cpy.fFillStyles[i];
      fRadiusOffsets[i] = cpy.fRadiusOffsets[i];
   }
}

//______________________________________________________________________________
TPie::~TPie()
{
   // Destructor.

   if (fNvals>0) {
      delete [] fVals;
      delete [] fLabels;
      delete [] fLineColors;
      delete [] fLineStyles;
      delete [] fLineWidths;
      delete [] fFillColors;
      delete [] fFillStyles;
      delete [] fRadiusOffsets;
   }

   if (fSlices) delete [] fSlices;
}


//______________________________________________________________________________
Int_t TPie::DistancetoPrimitive(Int_t px, Int_t py)
{
   // Evaluate the distance to the chart in gPad.

   if ( (fCurrent_slice = DistancetoSlice(px,py))>=0 ) {
      if (fCurrent_rad<=fRadius) {
         return 0;
      } else {
         Int_t tmpx = gPad->XtoPixel((fCurrent_rad-fRadius)*
                                     TMath::Cos(fCurrent_ang));
         Int_t tmpy = gPad->YtoPixel((fCurrent_rad-fRadius)*
                                     TMath::Sin(fCurrent_ang));
         return (Int_t) TMath::Sqrt(tmpx*tmpx+tmpy*tmpy);
      }
   } else {
      return 9999;
   }
}


//______________________________________________________________________________
Int_t TPie::DistancetoSlice(Int_t px, Int_t py)
{
   // Returns the slice number at the pixel position (px,py).
   // Returns -1 if no slice is picked.
   //
   // Used by DistancetoPrimitive.

   MakeSlices();

   Int_t result(-1);

   // coordinates
   Double_t xx = gPad->PadtoX(gPad->AbsPixeltoX(px));
   Double_t yy = gPad->PadtoY(gPad->AbsPixeltoY(py));

   Double_t phimin;
   Double_t cphi;
   Double_t phimax;
   for (Int_t i=0;i<fNvals;++i) {
      if (gIsUpt && fCurrent_slice!=i) continue;

      // Angles' values for this slice
      phimin = fSlices[2*i  ]*TMath::Pi()/180.;
      cphi   = fSlices[2*i+1]*TMath::Pi()/180.;
      phimax = fSlices[2*i+2]*TMath::Pi()/180.;

      // Recalculate slice's vertex' coordinates.
      Double_t x, y, rad, radOffset;
      if (gIsUpt) {
         x         = gX;
         y         = gY;
         rad       = gRadius,
         radOffset = gRadiusOffset;
      }
      else {
         x         = fX;
         y         = fY;
         rad       = fRadius;
         radOffset = fRadiusOffsets[i];
      }

      Double_t dx  = xx-x-radOffset*TMath::Cos(cphi);
      Double_t dy  = yy-y-radOffset*TMath::Sin(cphi);
      Double_t ang = TMath::ATan2(dy,dx);
      if (ang<0) ang += TMath::TwoPi();

      Double_t dist = TMath::Sqrt(dx*dx+dy*dy);

      if ( ((ang>=phimin && ang <= phimax) || (phimax>TMath::TwoPi() &&
             ang+TMath::TwoPi()>=phimin && ang+TMath::TwoPi()<phimax)) &&
             dist<=rad) { // if true the pointer is in the slice region
         fCurrent_x    = dx;
         fCurrent_y    = dy;
         fCurrent_ang  = ang;
         fCurrent_phi1 = phimin;
         fCurrent_phi2 = phimax;
         fCurrent_rad  = dist;
         result        = i;
         break;
      }
   }
   return result;
}


//______________________________________________________________________________
void TPie::Draw(Option_t *option)
{
   // Draw the pie chart.
   //
   // The possible options are listed in the TPie::Paint() method.

   TString soption(option);
   soption.ToLower();

   if (soption.Length()==0) soption = "l";

   if (gPad) {
      if (!gPad->IsEditable()) (gROOT->GetMakeDefCanvas())();
      if (!soption.Contains("same")) {
         gPad->Clear();
         gPad->Range(0.,0.,1.,1.);
      }
   }

   AppendPad(option);
}


//______________________________________________________________________________
void TPie::DrawGhost()
{
   // This method is for internal use. It is used by Execute event to draw the
   // outline of "this" TPie. Used when the opaque movements are not permitted.

   MakeSlices();

   // Loop over slices
   for (Int_t i=0;i<fNvals;++i) {
      Float_t minphi = (fSlices[i*2]+gAngularOffset+.5)*TMath::Pi()/180.;
      Float_t avgphi = fSlices[i*2+1]*TMath::Pi()/180.;
      Float_t maxphi = (fSlices[i*2+2]-.5)*TMath::Pi()/180;

      Double_t radOffset = (i == fCurrent_slice ? gRadiusOffset : fRadiusOffsets[i]);
      Double_t x0 = gX+radOffset*TMath::Cos(avgphi);
      Double_t y0 = gY+radOffset*TMath::Sin(avgphi);

      gVirtualX->DrawLine( gPad->XtoAbsPixel(x0), gPad->YtoAbsPixel(y0),
                           gPad->XtoAbsPixel(x0+gRadius*TMath::Cos(minphi)),
                           gPad->YtoAbsPixel(y0+gRadius*TMath::Sin(minphi)) );

      Int_t ndiv = 10;
      Double_t dphi = (maxphi-minphi)/ndiv;

      if (dphi>.15) ndiv = (Int_t) ((maxphi-minphi)/.15);
      dphi = (maxphi-minphi)/ndiv;

      // Loop to draw the arc
      for (Int_t j=1;j<ndiv;++j) {
         Double_t phi = minphi+dphi*j;
         gVirtualX->DrawLine( gPad->XtoAbsPixel(x0+gRadius*TMath::Cos(phi)),
                              gPad->YtoAbsPixel(y0+gRadius*TMath::Sin(phi)),
                              gPad->XtoAbsPixel(x0+gRadius*TMath::Cos(phi+dphi)),
                              gPad->YtoAbsPixel(y0+gRadius*TMath::Sin(phi+dphi)));
      }

      gVirtualX->DrawLine( gPad->XtoAbsPixel(x0+gRadius*TMath::Cos(maxphi)),
                           gPad->YtoAbsPixel(y0+gRadius*TMath::Sin(maxphi)),
                           gPad->XtoAbsPixel(x0), gPad->YtoAbsPixel(y0) );
   }
}


//______________________________________________________________________________
void TPie::ExecuteEvent(Int_t event, Int_t px, Int_t py)
{
   // Execute the mouse events.

   if (!gPad) return;
   if (!gPad->IsEditable() && event != kMouseEnter) return;

   MakeSlices();

   static bool isMovingPie(kFALSE);
   static bool isMovingSlice(kFALSE);
   static bool isResizing(kFALSE);
   static bool isRotating(kFALSE);
   static bool onBorder(kFALSE);
   bool isRedrawing(kFALSE);
   static Int_t prev_event(-1);
   static Int_t oldpx, oldpy;

   // Portion of pie considered as "border"
   static Double_t dr     = gPad->PixeltoX(3);
   static Double_t minRad = gPad->PixeltoX(10);

   // Angular divisions in radial direction
   static Double_t angstep1 = 0.5*TMath::PiOver4();
   static Double_t angstep2 = 1.5*TMath::PiOver4();
   static Double_t angstep3 = 2.5*TMath::PiOver4();
   static Double_t angstep4 = 3.5*TMath::PiOver4();
   static Double_t angstep5 = 4.5*TMath::PiOver4();
   static Double_t angstep6 = 5.5*TMath::PiOver4();
   static Double_t angstep7 = 6.5*TMath::PiOver4();
   static Double_t angstep8 = 7.5*TMath::PiOver4();

   Int_t dx, dy;
   Double_t mdx, mdy;

   switch(event) {
      case kButton1Down:
         // Change cursor to show pie's movement.
         gVirtualX->SetLineColor(1);
         gVirtualX->SetLineWidth(2);

         // Current center and radius.
         gX             = fX;
         gY             = fY;
         gRadius        = fRadius;
         gRadiusOffset  = fRadiusOffsets[fCurrent_slice];
         gAngularOffset = 0;
         gIsUpt         = kTRUE;

         prev_event = kButton1Down;

      case kMouseMotion:
         if (fCurrent_rad>=fRadius-2.*dr && fCurrent_rad<=fRadius+dr
               && !isMovingPie && !isMovingSlice && !isResizing) {
            if (fCurrent_ang>=angstep8 || fCurrent_ang<angstep1)
               gPad->SetCursor(kRightSide);
            else if (fCurrent_ang>=angstep1 && fCurrent_ang<angstep2)
               gPad->SetCursor(kTopRight);
            else if (fCurrent_ang>=angstep2 && fCurrent_ang<angstep3)
               gPad->SetCursor(kTopSide);
            else if (fCurrent_ang>=angstep3 && fCurrent_ang<angstep4)
               gPad->SetCursor(kTopLeft);
            else if (fCurrent_ang>=angstep4 && fCurrent_ang<=angstep5)
               gPad->SetCursor(kLeftSide);
            else if (fCurrent_ang>=angstep5 && fCurrent_ang<angstep6)
               gPad->SetCursor(kBottomLeft);
            else if (fCurrent_ang>=angstep6 && fCurrent_ang<angstep7)
               gPad->SetCursor(kBottomSide);
            else if (fCurrent_ang>=angstep7 && fCurrent_ang<angstep8)
               gPad->SetCursor(kBottomRight);
            onBorder = kTRUE;
         } else {
            onBorder = kFALSE;
            if (fCurrent_rad>fRadius*.6) {
               gPad->SetCursor(kPointer);
            } else if (fCurrent_rad<=fRadius*.3) {
               gPad->SetCursor(kHand);
            } else if (fCurrent_rad<=fRadius*.6 && fCurrent_rad>=fRadius*.3) {
               gPad->SetCursor(kRotate);
            }
         }
         oldpx = px;
         oldpy = py;
         if (isMovingPie || isMovingSlice) gPad->SetCursor(kMove);
         break;

      case kButton1Motion:
         if (!isMovingSlice || !isMovingPie || !isResizing) {
            if (prev_event==kButton1Down) {
               if (onBorder) {
                  isResizing = kTRUE;
               } else if (fCurrent_rad>=fRadius*.6 && fCurrent_slice>=0) {
                  isMovingSlice = kTRUE;
               } else if (fCurrent_rad<=fRadius*.3) {
                  isMovingPie = kTRUE;
               } else if (fCurrent_rad<fRadius*.6 && fCurrent_rad>fRadius*.3) {
                  isRotating = kTRUE;
               }
            }
         }

         dx = px-oldpx;
         dy = py-oldpy;

         mdx = gPad->PixeltoX(dx);
         mdy = gPad->PixeltoY(dy);

         if (isMovingPie || isMovingSlice) {
            gPad->SetCursor(kMove);
            if (isMovingSlice) {
               Float_t avgphi = fSlices[fCurrent_slice*2+1]*TMath::Pi()/180.;

               if (!gPad->OpaqueMoving()) DrawGhost();

               gRadiusOffset += TMath::Cos(avgphi)*mdx +TMath::Sin(avgphi)*mdy;
               if (gRadiusOffset<0) gRadiusOffset = .0;

               if (!gPad->OpaqueMoving()) DrawGhost();
            } else {
               if (!gPad->OpaqueMoving()) DrawGhost();

               gX += mdx;
               gY += mdy;

               if (!gPad->OpaqueMoving()) DrawGhost();
            }
         } else if (isResizing) {
            if (!gPad->OpaqueResizing()) DrawGhost();

            Float_t dr = mdx*TMath::Cos(fCurrent_ang)+mdy*TMath::Sin(fCurrent_ang);
            if (gRadius+dr>=minRad) {
               gRadius += dr;
            } else {
               gRadius = minRad;
            }

            if (!gPad->OpaqueResizing()) DrawGhost();
         } else if (isRotating) {
            if (!gPad->OpaqueMoving()) DrawGhost();

            if (py>=gPad->YtoAbsPixel(fY)) {
               if (px<oldpx) gAngularOffset--;
               else          gAngularOffset++;
            } else {
               if (px<oldpx) gAngularOffset++;
               else          gAngularOffset--;
            }
            if (gAngularOffset>=360) gAngularOffset = 0;
            if (!gPad->OpaqueMoving()) DrawGhost();
         }

         oldpx = px;
         oldpy = py;
         if ( ((isMovingPie || isMovingSlice) && gPad->OpaqueMoving()) ||
               (isResizing && gPad->OpaqueResizing()) ||
               (isRotating && gPad->OpaqueMoving()) ) isRedrawing = kTRUE;
         else break;

      case kButton1Up:
         if (!isRedrawing) {
            prev_event = kButton1Up;
            gIsUpt = kFALSE;
         }

         if (gROOT->IsEscaped()) {
            gROOT->SetEscape(kFALSE);
            gIsUpt = kFALSE;
            break;
         }

         fX      = gX;
         fY      = gY;
         fRadius = gRadius;
         fRadiusOffsets[fCurrent_slice] = gRadiusOffset;
         SetAngularOffset(fAngularOffset+gAngularOffset);

         gPad->Modified(kTRUE);

         gVirtualX->SetLineColor(-1);
         gVirtualX->SetLineWidth(-1);

         if (isMovingPie)   isMovingPie   = kFALSE;
         if (isMovingSlice) isMovingSlice = kFALSE;
         if (isResizing)    isResizing    = kFALSE;
         if (isRotating)    isResizing    = kFALSE;

         gIsUpt = kFALSE;
         break;

      case kMouseEnter:
         break;

      case kButton1Locate:
         break;

      default:
         // unknown event
         break;
   }
}


//______________________________________________________________________________
const char* TPie::GetEntryLabel(Int_t i)
{
   // Returns the label of the entry number "i".

   return fLabels[i].Data();
}


//______________________________________________________________________________
Int_t TPie::GetEntryFillColor(Int_t i)
{
   // Return the color of the slice number "i".

   return fFillColors[i];
}


//______________________________________________________________________________
Int_t TPie::GetEntryFillStyle(Int_t i)
{
   // Return the style use to fill the slice number "i".

   return fFillStyles[i];
}


//______________________________________________________________________________
Double_t TPie::GetEntryRadiusOffset(Int_t i)
{
   // Return the radial offset's value for the slice number "i".

   return fRadiusOffsets[i];
}


//______________________________________________________________________________
Double_t TPie::GetEntryVal(Int_t i)
{
   // Return the value associated with the slice number "i".

   return fVals[i];
}


//______________________________________________________________________________
void TPie::Init(Int_t np, Double_t ao, Double_t x, Double_t y, Double_t r)
{
   // Common initialization for all constructors.
   // This is a private function called to allocate the memory.

   gIsUpt = kFALSE;

   fAngularOffset = ao;
   fX             = x;
   fY             = y;
   fRadius        = r;
   fNvals         = np;
   fSum           = 0.;
   fSlices        = 0;

   fLabelsOffset = gStyle->GetLabelOffset();

   fLineColors = new Int_t[fNvals];
   fLineStyles = new Int_t[fNvals];
   fLineWidths = new Int_t[fNvals];

   fFillColors = new Int_t[fNvals];
   fFillStyles = new Int_t[fNvals];

   fLabels        = new TString[fNvals];
   fRadiusOffsets = new Double_t[fNvals];
   fVals          = new Double_t[fNvals];

   for (Int_t i=0;i<fNvals;++i) {
      fVals[i]          = 0.;
      fLabels[i]        = "Slice";
      fLabels[i]       += i;
      fLineColors[i]    = 1;
      fLineStyles[i]    = 1;
      fLineWidths[i]    = 1;
      fFillColors[i]    = gStyle->GetColorPalette(i);
      fFillStyles[i]    = 1001;
      fRadiusOffsets[i] = 0.;
   }

   fLabelFormat    = "%txt";
   fFractionFormat = "%3.2f";
   fValFormat      = "%4.2f";
   fPercentFormat  = "%3.1f";
}


//______________________________________________________________________________
void TPie::Paint(Option_t *option)
{
   // Paint a Pie chart in a canvas.
   // The possible option are:
   //
   // "R"  Print the labels along the central "R"adius of slices.
   // "T"  Print the label in a direction "T"angent to circle that describes
   //      the TPie.
   //
   // Other options changing the labels' format are described in
   // TPie::SetLabelFormat().

   MakeSlices();

   TString soption(option);

   bool optionSame(kFALSE);

   // For the label orientation there are 3 possibilities:
   //   0: horizontal
   //   1: radial
   //   2: tangent
   Int_t lblor(0);

   // Parse the options
   Int_t idx;
   // Paint the TPie in an existing canvas
   if ( (idx=soption.Index("same"))>=0 ) {
      optionSame = kTRUE;
      soption.Remove(idx,4);
   }

   // Seek if have to draw the labels around the pie chart
   if ( (idx=soption.Index("t"))>=0 ) {
      lblor = 2;
      soption.Remove(idx,1);
   }

   // Seek if have to paint the labels along the radii
   if ( (idx=soption.Index("r"))>=0 ) {
      lblor = 1;
      soption.Remove(idx,1);
   }

   if (fNvals<=0) {
      Warning("Paint","No vals");
      return;
   }

   if (!fVals) {
      Warning("TPie","No valid arrays of values");
      return;
   }

   // Objects useful to draw labels and slices
   TLatex *textlabel = new TLatex();
   TArc *arc = new TArc();

   // Draw the slices.
   for (Int_t i=0;i<fNvals;++i) {
      // Set the color of the next slice
      arc->SetFillColor(fFillColors[i]);
      arc->SetFillStyle(fFillStyles[i]);
      arc->SetLineColor(fLineColors[i]);
      arc->SetLineStyle(fLineStyles[i]);
      arc->SetLineWidth(fLineWidths[i]);

      // Paint the slice
      Float_t aphi = fSlices[2*i+1]*TMath::Pi()/180.;
      Double_t ax = fX+TMath::Cos(aphi)*fRadiusOffsets[i];
      Double_t ay = fY+TMath::Sin(aphi)*fRadiusOffsets[i];
      arc->PaintEllipse(ax, ay, fRadius, fRadius, fSlices[2*i],
                                                  fSlices[2*i+2], 0.);
   }


   // Loop to place the labels.
   for (Int_t i=0;i<fNvals;++i) {
      Float_t aphi = fSlices[2*i+1]*TMath::Pi()/180.;
      Float_t label_off = fLabelsOffset;

      // Set the font
      textlabel->SetTextFont(GetTextFont());
      textlabel->SetTextSize(GetTextSize());
      textlabel->SetTextColor(GetTextColor());

      // Paint the text in the pad
      TString tmptxt  = fLabelFormat;

      tmptxt.ReplaceAll("%txt",fLabels[i]);
      tmptxt.ReplaceAll("%val",Form(fValFormat.Data(),fVals[i]));
      tmptxt.ReplaceAll("%frac",Form(fFractionFormat.Data(),fVals[i]/fSum));
      tmptxt.ReplaceAll("%perc",Form("%3.1f %s",(fVals[i]/fSum)*100,"%"));

      textlabel->SetTitle(tmptxt.Data());
      Double_t h = textlabel->GetYsize();
      Double_t w = textlabel->GetXsize();

      Float_t lx = fX+(fRadius+fRadiusOffsets[i]+label_off)*TMath::Cos(aphi);
      Float_t ly = fY+(fRadius+fRadiusOffsets[i]+label_off)*TMath::Sin(aphi);

      Double_t lblang = 0;

      if (lblor==1) {
         lblang += aphi;

         lx += h/2.*TMath::Sin(lblang);
         ly -= h/2.*TMath::Cos(lblang);

         // This control prevent text up-side
         if (aphi>TMath::PiOver2() && aphi<=3.*TMath::PiOver2()) {
            lx     += w*TMath::Cos(lblang)-h*TMath::Sin(lblang);
            ly     += w*TMath::Sin(lblang)+h*TMath::Cos(lblang);
            lblang -= TMath::Pi();
         }
      } else if (lblor==2) {
         lblang += aphi-TMath::PiOver2();
         if (lblang<0) lblang+=TMath::TwoPi();

{'fraction_non_alphanumeric': 0.13402339627725263, 'fraction_numerical': 0.013561757639715294, 'mean_word_length': 2.338180182881127, 'pattern_counts': {'":': 0, '<': 99, '<?xml version=': 0, '>': 249, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 18, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3051323', 'n_tokens_mistral': 15073, 'n_tokens_neox': 14437, 'n_words': 3052}