// @(#)root/graf:$Name:  $:$Id: TEllipse.cxx,v 1.27 2006/07/03 16:10:45 brun Exp $
// Author: Rene Brun   16/10/95

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

#include <stdlib.h>

#include "Riostream.h"
#include "TROOT.h"
#include "TEllipse.h"
#include "TVirtualPad.h"
#include "TMath.h"
#include "TClass.h"

const Double_t kPI = 3.14159265358979323846;

ClassImp(TEllipse)


//______________________________________________________________________________
//  Ellipse class.
//
//  Ellipse is a general ellipse that can be truncated and rotated.
//  An Ellipse is defined by its center (x1,y1) and two radius r1 and r2.
//  A minimum and maximum angle may be specified (phimin, phimax).
//  The Ellipse may be rotated with an angle theta.
//  The attributes of the outline line are given via TAttLine.
//  The attributes of the fill area are given via TAttFill.
//  The picture below illustrates different types of ellipses.
//
//  When an ellipse sector only is drawn, the lines connecting the center
//  of the ellipse to the edges are drawn by default. One can specify
//  the drawing option "only" to not draw these lines or alternatively
//  call the function SetNoEdges().
//
//Begin_Html
/*
<img src="gif/ellipse.gif">
*/
//End_Html
//


//______________________________________________________________________________
TEllipse::TEllipse(): TObject(), TAttLine(), TAttFill()
{
   // Ellipse default constructor.

   fX1 = 0;
   fY1 = 0;
   fR1 = 1;
   fR2 = 1;
   fPhimin = 0;
   fPhimax = 360;
   fTheta  = 0;
}


//______________________________________________________________________________
TEllipse::TEllipse(Double_t x1, Double_t y1,Double_t r1,Double_t r2,Double_t phimin,Double_t phimax,Double_t theta)
      :TObject(), TAttLine(), TAttFill(0,1001)
{
   // Ellipse normal constructor.

   fX1     = x1;
   fY1     = y1;
   fR1     = r1;
   fR2     = r2;
   fPhimin = phimin;
   fPhimax = phimax;
   fTheta  = theta;
   if (r2 <= 0) fR2 = fR1;
}


//______________________________________________________________________________
TEllipse::~TEllipse()
{
   // Ellipse default destructor.

}


//______________________________________________________________________________
TEllipse::TEllipse(const TEllipse &ellipse) : TObject(ellipse), TAttLine(ellipse), TAttFill(ellipse)
{
   // Copy constructor.

   ((TEllipse&)ellipse).Copy(*this);
}


//______________________________________________________________________________
void TEllipse::Copy(TObject &obj) const
{
   // Copy this ellipse to ellipse.

   TObject::Copy(obj);
   TAttLine::Copy(((TEllipse&)obj));
   TAttFill::Copy(((TEllipse&)obj));
   ((TEllipse&)obj).fX1 = fX1;
   ((TEllipse&)obj).fY1 = fY1;
   ((TEllipse&)obj).fR1 = fR1;
   ((TEllipse&)obj).fR2 = fR2;
   ((TEllipse&)obj).fPhimin = fPhimin;
   ((TEllipse&)obj).fPhimax = fPhimax;
   ((TEllipse&)obj).fTheta  = fTheta;
}


//______________________________________________________________________________
Int_t TEllipse::DistancetoPrimitive(Int_t px, Int_t py)
{
   // Compute distance from point px,py to an ellipse.
   //
   //  Compute the closest distance of approach from point px,py to this ellipse.
   //  The distance is computed in pixels units.

   Double_t x = gPad->PadtoX(gPad->AbsPixeltoX(px));
   Double_t y = gPad->PadtoY(gPad->AbsPixeltoY(py));

   Double_t dxnr = x - fX1;
   Double_t dynr = y - fY1;

   Double_t ct = TMath::Cos(kPI*GetTheta()/180.0);
   Double_t st = TMath::Sin(kPI*GetTheta()/180.0);

   Double_t dx =  dxnr*ct + dynr*st;
   Double_t dy = -dxnr*st + dynr*ct;

   Double_t r1 = fR1;
   Double_t r2 = fR2;

   if (dx == 0 || r1 == 0 || r2 == 0) return 9999;
   Double_t distp = TMath::Sqrt(dx*dx + dy*dy);

   Double_t tana = dy/dx;
   tana *= tana;
   Double_t distr = TMath::Sqrt((1+tana)/(1.0/(r1*r1) + tana/(r2*r2)));
   Int_t dist = 9999;
   if (GetFillColor() && GetFillStyle()) {
      if (distr > distp) dist = 0;
   } else {
      if (TMath::Abs(distr-distp)/(r1+r2) < 0.01) dist = 0;
   }
   return dist;
}


//______________________________________________________________________________
void TEllipse::Draw(Option_t *option)
{
   // Draw this ellipse with its current attributes.

   AppendPad(option);

}


//______________________________________________________________________________
void TEllipse::DrawEllipse(Double_t x1, Double_t y1,Double_t r1,Double_t r2,Double_t phimin,Double_t phimax,Double_t theta,Option_t *option)
{
   // Draw this ellipse with new coordinates.

   TEllipse *newellipse = new TEllipse(x1, y1, r1, r2, phimin, phimax,theta);
   TAttLine::Copy(*newellipse);
   TAttFill::Copy(*newellipse);
   newellipse->SetBit(kCanDelete);
   newellipse->AppendPad(option);
   if (TestBit(kNoEdges)) newellipse->SetBit(kNoEdges);
}


//______________________________________________________________________________
void TEllipse::ExecuteEvent(Int_t event, Int_t px, Int_t py)
{
   // Execute action corresponding to one event.
   //
   //  This member function is called when a line is clicked with the locator
   //
   //  If Left button clicked on one of the line end points, this point
   //     follows the cursor until button is released.
   //
   //  if Middle button clicked, the line is moved parallel to itself
   //     until the button is released.
   //
   //  NOTE that support for log scale is not implemented

   Int_t kMaxDiff = 10;
   const Int_t kMinSize = 25;
   const Int_t np = 40;
   static Int_t x[np+2], y[np+2];
   static Int_t px1,py1,npe,r1,r2,sav1,sav2;
   static Int_t pxold, pyold;
   static Int_t sig,impair;
   Int_t i, dpx, dpy;
   Double_t angle,dx,dy,dphi,ct,st,fTy,fBy,fLx,fRx;
   static Bool_t pTop, pL, pR, pBot, pINSIDE;
   static Int_t pTx,pTy,pLx,pLy,pRx,pRy,pBx,pBy;

   if (!gPad->IsEditable()) return;

   switch (event) {

   case kButton1Down:
      gVirtualX->SetLineColor(-1);
      TAttLine::Modify();
      dphi = (fPhimax-fPhimin)*kPI/(180*np);
      ct   = TMath::Cos(kPI*fTheta/180);
      st   = TMath::Sin(kPI*fTheta/180);
      for (i=0;i<np;i++) {
         angle = fPhimin*kPI/180 + Double_t(i)*dphi;
         dx    = fR1*TMath::Cos(angle);
         dy    = fR2*TMath::Sin(angle);
         x[i]  = gPad->XtoAbsPixel(fX1 + dx*ct - dy*st);
         y[i]  = gPad->YtoAbsPixel(fY1 + dx*st + dy*ct);
      }
      if (fPhimax-fPhimin >= 360 ) {
         x[np] = x[0];
         y[np] = y[0];
         npe = np;
      } else {
         x[np]   = gPad->XtoAbsPixel(fX1);
         y[np]   = gPad->YtoAbsPixel(fY1);
         x[np+1] = x[0];
         y[np+1] = y[0];
         npe = np + 1;
      }
      impair = 0;
      px1 = gPad->XtoAbsPixel(fX1);
      py1 = gPad->YtoAbsPixel(fY1);
      pTx = pBx = px1;
      pLy = pRy = py1;
      pTy = gPad->YtoAbsPixel(fR2+fY1);
      pBy = gPad->YtoAbsPixel(-fR2+fY1);
      pLx = gPad->XtoAbsPixel(-fR1+fX1);
      pRx = gPad->XtoAbsPixel(fR1+fX1);
      r2 = (pBy-pTy)/2;
      r1 = (pRx-pLx)/2;
      gVirtualX->DrawLine(pRx+4, py1+4, pRx-4, py1+4);
      gVirtualX->DrawLine(pRx-4, py1+4, pRx-4, py1-4);
      gVirtualX->DrawLine(pRx-4, py1-4, pRx+4, py1-4);
      gVirtualX->DrawLine(pRx+4, py1-4, pRx+4, py1+4);
      gVirtualX->DrawLine(pLx+4, py1+4, pLx-4, py1+4);
      gVirtualX->DrawLine(pLx-4, py1+4, pLx-4, py1-4);
      gVirtualX->DrawLine(pLx-4, py1-4, pLx+4, py1-4);
      gVirtualX->DrawLine(pLx+4, py1-4, pLx+4, py1+4);
      gVirtualX->DrawLine(px1+4, pBy+4, px1-4, pBy+4);
      gVirtualX->DrawLine(px1-4, pBy+4, px1-4, pBy-4);
      gVirtualX->DrawLine(px1-4, pBy-4, px1+4, pBy-4);
      gVirtualX->DrawLine(px1+4, pBy-4, px1+4, pBy+4);
      gVirtualX->DrawLine(px1+4, pTy+4, px1-4, pTy+4);
      gVirtualX->DrawLine(px1-4, pTy+4, px1-4, pTy-4);
      gVirtualX->DrawLine(px1-4, pTy-4, px1+4, pTy-4);
      gVirtualX->DrawLine(px1+4, pTy-4, px1+4, pTy+4);
      // No break !!!

   case kMouseMotion:
      px1 = gPad->XtoAbsPixel(fX1);
      py1 = gPad->YtoAbsPixel(fY1);
      pTx = pBx = px1;
      pLy = pRy = py1;
      pTy = gPad->YtoAbsPixel(fR2+fY1);
      pBy = gPad->YtoAbsPixel(-fR2+fY1);
      pLx = gPad->XtoAbsPixel(-fR1+fX1);
      pRx = gPad->XtoAbsPixel(fR1+fX1);
      pTop = pL = pR = pBot = pINSIDE = kFALSE;
      if ((TMath::Abs(px - pTx) < kMaxDiff) &&
          (TMath::Abs(py - pTy) < kMaxDiff)) {             // top edge
         pTop = kTRUE;
         gPad->SetCursor(kTopSide);
      }
      else
      if ((TMath::Abs(px - pBx) < kMaxDiff) &&
          (TMath::Abs(py - pBy) < kMaxDiff)) {             // bottom edge
         pBot = kTRUE;
         gPad->SetCursor(kBottomSide);
      }
      else
      if ((TMath::Abs(py - pLy) < kMaxDiff) &&
          (TMath::Abs(px - pLx) < kMaxDiff)) {             // left edge
         pL = kTRUE;
         gPad->SetCursor(kLeftSide);
      }
      else
      if ((TMath::Abs(py - pRy) < kMaxDiff) &&
          (TMath::Abs(px - pRx) < kMaxDiff)) {             // right edge
         pR = kTRUE;
         gPad->SetCursor(kRightSide);
      }
      else {pINSIDE= kTRUE; gPad->SetCursor(kMove); }
      pxold = px;  pyold = py;

      break;

   case kButton1Motion:
      gVirtualX->DrawLine(pRx+4, py1+4, pRx-4, py1+4);
      gVirtualX->DrawLine(pRx-4, py1+4, pRx-4, py1-4);
      gVirtualX->DrawLine(pRx-4, py1-4, pRx+4, py1-4);
      gVirtualX->DrawLine(pRx+4, py1-4, pRx+4, py1+4);
      gVirtualX->DrawLine(pLx+4, py1+4, pLx-4, py1+4);
      gVirtualX->DrawLine(pLx-4, py1+4, pLx-4, py1-4);
      gVirtualX->DrawLine(pLx-4, py1-4, pLx+4, py1-4);
      gVirtualX->DrawLine(pLx+4, py1-4, pLx+4, py1+4);
      gVirtualX->DrawLine(px1+4, pBy+4, px1-4, pBy+4);
      gVirtualX->DrawLine(px1-4, pBy+4, px1-4, pBy-4);
      gVirtualX->DrawLine(px1-4, pBy-4, px1+4, pBy-4);
      gVirtualX->DrawLine(px1+4, pBy-4, px1+4, pBy+4);
      gVirtualX->DrawLine(px1+4, pTy+4, px1-4, pTy+4);
      gVirtualX->DrawLine(px1-4, pTy+4, px1-4, pTy-4);
      gVirtualX->DrawLine(px1-4, pTy-4, px1+4, pTy-4);
      gVirtualX->DrawLine(px1+4, pTy-4, px1+4, pTy+4);
      for (i=0;i<npe;i++) gVirtualX->DrawLine(x[i], y[i], x[i+1], y[i+1]);
      if (pTop) {
         sav1 = py1;
         sav2 = r2;
         py1 += (py - pyold)/2;
         r2 -= (py - pyold)/2;
         if (TMath::Abs(pyold-py)%2==1) impair++;
         if (py-pyold>0) sig=+1;
         else sig=-1;
         if (impair==2) { impair = 0; py1 += sig; r2 -= sig;}
         if (py1 > pBy-kMinSize) {py1 = sav1; r2 = sav2; py = pyold;}
      }
      if (pBot) {
         sav1 = py1;
         sav2 = r2;
         py1 += (py - pyold)/2;
         r2 += (py - pyold)/2;
         if (TMath::Abs(pyold-py)%2==1) impair++;
         if (py-pyold>0) sig=+1;
         else sig=-1;
         if (impair==2) { impair = 0; py1 += sig; r2 += sig;}
         if (py1 < pTy+kMinSize) {py1 = sav1; r2 = sav2; py = pyold;}
      }
      if (pL) {
         sav1 = px1;
         sav2 = r1;
         px1 += (px - pxold)/2;
         r1 -= (px - pxold)/2;
         if (TMath::Abs(pxold-px)%2==1) impair++;
         if (px-pxold>0) sig=+1;
         else sig=-1;
         if (impair==2) { impair = 0; px1 += sig; r1 -= sig;}
         if (px1 > pRx-kMinSize) {px1 = sav1; r1 = sav2; px = pxold;}
      }
      if (pR) {
         sav1 = px1;
         sav2 = r1;
         px1 += (px - pxold)/2;
         r1 += (px - pxold)/2;
         if (TMath::Abs(pxold-px)%2==1) impair++;
         if (px-pxold>0) sig=+1;
         else sig=-1;
         if (impair==2) { impair = 0; px1 += sig; r1 += sig;}
         if (px1 < pLx+kMinSize) {px1 = sav1; r1 = sav2; px = pxold;}
      }
      if (pTop || pBot || pL || pR) {
         gVirtualX->SetLineColor(-1);
         TAttLine::Modify();
         dphi = (fPhimax-fPhimin)*kPI/(180*np);
         ct   = TMath::Cos(kPI*fTheta/180);
         st   = TMath::Sin(kPI*fTheta/180);
         for (i=0;i<np;i++) {
            angle = fPhimin*kPI/180 + Double_t(i)*dphi;
            dx    = r1*TMath::Cos(angle);
            dy    = r2*TMath::Sin(angle);
            x[i]  = px1 + Int_t(dx*ct - dy*st);
            y[i]  = py1 + Int_t(dx*st + dy*ct);
         }
         if (fPhimax-fPhimin >= 360 ) {
            x[np] = x[0];
            y[np] = y[0];
            npe = np;
         } else {
            x[np]   = px1;
            y[np]   = py1;
            x[np+1] = x[0];
            y[np+1] = y[0];
            npe = np + 1;
         }
         for (i=0;i<npe;i++) gVirtualX->DrawLine(x[i], y[i], x[i+1], y[i+1]);
      }
      if (pINSIDE) {
         dpx  = px-pxold;  dpy = py-pyold;
         px1 += dpx; py1 += dpy;
         for (i=0;i<=npe;i++) { x[i] += dpx; y[i] += dpy;}
         for (i=0;i<npe;i++) gVirtualX->DrawLine(x[i], y[i], x[i+1], y[i+1]);
      }
      pTx = pBx = px1;
      pRx = px1+r1;
      pLx = px1-r1;
      pRy = pLy = py1;
      pTy = py1-r2;
      pBy = py1+r2;
      gVirtualX->DrawLine(pRx+4, py1+4, pRx-4, py1+4);
      gVirtualX->DrawLine(pRx-4, py1+4, pRx-4, py1-4);
      gVirtualX->DrawLine(pRx-4, py1-4, pRx+4, py1-4);
      gVirtualX->DrawLine(pRx+4, py1-4, pRx+4, py1+4);
      gVirtualX->DrawLine(pLx+4, py1+4, pLx-4, py1+4);
      gVirtualX->DrawLine(pLx-4, py1+4, pLx-4, py1-4);
      gVirtualX->DrawLine(pLx-4, py1-4, pLx+4, py1-4);
      gVirtualX->DrawLine(pLx+4, py1-4, pLx+4, py1+4);
      gVirtualX->DrawLine(px1+4, pBy+4, px1-4, pBy+4);
      gVirtualX->DrawLine(px1-4, pBy+4, px1-4, pBy-4);
      gVirtualX->DrawLine(px1-4, pBy-4, px1+4, pBy-4);
      gVirtualX->DrawLine(px1+4, pBy-4, px1+4, pBy+4);
      gVirtualX->DrawLine(px1+4, pTy+4, px1-4, pTy+4);
      gVirtualX->DrawLine(px1-4, pTy+4, px1-4, pTy-4);
      gVirtualX->DrawLine(px1-4, pTy-4, px1+4, pTy-4);
      gVirtualX->DrawLine(px1+4, pTy-4, px1+4, pTy+4);
      pxold = px;
      pyold = py;
      break;

   case kButton1Up:
      if (gROOT->IsEscaped()) {
         gROOT->SetEscape(kFALSE);
         break;
      }

      fX1 = gPad->AbsPixeltoX(px1);
      fY1 = gPad->AbsPixeltoY(py1);
      fBy = gPad->AbsPixeltoY(py1+r2);
      fTy = gPad->AbsPixeltoY(py1-r2);
      fLx = gPad->AbsPixeltoX(px1+r1);
      fRx = gPad->AbsPixeltoX(px1-r1);
      fR1 = TMath::Abs(fRx-fLx)/2;
      fR2 = TMath::Abs(fTy-fBy)/2;
      gPad->Modified(kTRUE);
      gVirtualX->SetLineColor(-1);
   }
}


//______________________________________________________________________________
void TEllipse::ls(Option_t *) const
{
   // List this ellipse with its attributes.

   TROOT::IndentLevel();
   printf("%s:  X1= %f Y1=%f R1=%f R2=%f\n",GetName(),fX1,fY1,fR1,fR2);
}


//______________________________________________________________________________
void TEllipse::Paint(Option_t *option)
{
   // Paint this ellipse with its current attributes.

   PaintEllipse(fX1,fY1,fR1,fR2,fPhimin,fPhimax,fTheta,option);
}


//______________________________________________________________________________
void TEllipse::PaintEllipse(Double_t x1, Double_t y1, Double_t r1, Double_t r2,
                            Double_t phimin, Double_t phimax, Double_t theta,
                            Option_t *option)
{
   // Draw this ellipse with new coordinates.

   const Int_t np = 200;
   static Double_t x[np+3], y[np+3];
   TAttLine::Modify();  //Change line attributes only if necessary
   TAttFill::Modify();  //Change fill attributes only if necessary

   //set number of points approximatively proportional to the ellipse circumference
   Double_t circ = kPI*(r1+r2)*(phimax-phimin)/360;
   Int_t n = (Int_t)(np*circ/((gPad->GetX2()-gPad->GetX1())+(gPad->GetY2()-gPad->GetY1())));
   if (n < 8) n= 8;
   if (n > np) n = np;
   Double_t angle,dx,dy;
   Double_t dphi = (phimax-phimin)*kPI/(180*n);
   Double_t ct   = TMath::Cos(kPI*theta/180);
   Double_t st   = TMath::Sin(kPI*theta/180);
   for (Int_t i=0;i<=n;i++) {
      angle = phimin*kPI/180 + Double_t(i)*dphi;
      dx    = r1*TMath::Cos(angle);
      dy    = r2*TMath::Sin(angle);
      x[i]  = gPad->XtoPad(x1 + dx*ct - dy*st);
      y[i]  = gPad->YtoPad(y1 + dx*st + dy*ct);
   }
   TString opt = option;
   opt.ToLower();
   if (phimax-phimin >= 360 ) {
      if (GetFillColor()) gPad->PaintFillArea(n,x,y);
      if (GetLineStyle()) gPad->PaintPolyLine(n+1,x,y);
   } else {
      x[n+1] = gPad->XtoPad(x1);
      y[n+1] = gPad->YtoPad(y1);
      x[n+2] = x[0];
      y[n+2] = y[0];
      if (GetFillStyle()) gPad->PaintFillArea(n+2,x,y);
      if (GetLineStyle()) {
         if (TestBit(kNoEdges) || opt.Contains("only")) gPad->PaintPolyLine(n+1,x,y);
         else                                           gPad->PaintPolyLine(n+3,x,y);
      }
   }
}


//______________________________________________________________________________
void TEllipse::Print(Option_t *) const
{
   // Dump this ellipse with its attributes.

   printf("Ellipse:  X1=%f Y1=%f R1=%f R2=%f",fX1,fY1,fR1,fR2);
   if (GetLineColor() != 1) printf(" Color=%d",GetLineColor());
   if (GetLineStyle() != 1) printf(" Style=%d",GetLineStyle());
   if (GetLineWidth() != 1) printf(" Width=%d",GetLineWidth());
   printf("\n");
}


//______________________________________________________________________________
void TEllipse::SavePrimitive(ostream &out, Option_t * /*= ""*/)
{
   // Save primitive as a C++ statement(s) on output stream out

   out<<"   "<<endl;
   if (gROOT->ClassSaved(TEllipse::Class())) {
      out<<"   ";
   } else {
      out<<"   TEllipse *";
   }
   out<<"ellipse = new TEllipse("<<fX1<<","<<fY1<<","<<fR1<<","<<fR2
      <<","<<fPhimin<<","<<fPhimax<<","<<fTheta<<");"<<endl;

   SaveFillAttributes(out,"ellipse",0,1001);
   SaveLineAttributes(out,"ellipse",1,1,1);

   if (GetNoEdges()) out<<"   ellipse->SetNoEdges();"<<endl;

   out<<"   ellipse->Draw();"<<endl;
}


//______________________________________________________________________________
Bool_t TEllipse::GetNoEdges() const
{
   // Return kTRUE if kNoEdges bit is set, kFALSE otherwise.

   return TestBit(kNoEdges) ? kTRUE : kFALSE;
}


//______________________________________________________________________________
void TEllipse::SetNoEdges(Bool_t noEdges)
{
   // if  noEdges = kTRUE the lines connecting the center to the edges
   // will not be drawn.
   // default is to draw the edges.

   if (noEdges) SetBit(kNoEdges);
   else         ResetBit(kNoEdges);
}


//______________________________________________________________________________
void TEllipse::Streamer(TBuffer &R__b)
{
   // Stream an object of class TEllipse.

   if (R__b.IsReading()) {
      UInt_t R__s, R__c;
      Version_t R__v = R__b.ReadVersion(&R__s, &R__c);
      if (R__v > 1) {
         TEllipse::Class()->ReadBuffer(R__b, this, R__v, R__s, R__c);
         return;
      }
      //====process old versions before automatic schema evolution
      TObject::Streamer(R__b);
      TAttLine::Streamer(R__b);
      TAttFill::Streamer(R__b);
      Float_t x1,y1,r1,r2,phimin,phimax,theta;
      R__b >> x1;     fX1 = x1;
      R__b >> y1;     fY1 = y1;
      R__b >> r1;     fR1 = r1;
      R__b >> r2;     fR2 = r2;
      R__b >> phimin; fPhimin = phimin;
      R__b >> phimax; fPhimax = phimax;
      R__b >> theta;  fTheta  = theta;
      R__b.CheckByteCount(R__s, R__c, TEllipse::IsA());
      //====end of old versions

   } else {
      TEllipse::Class()->WriteBuffer(R__b,this);
   }
}


ROOT page - Class index - Class Hierarchy - Top of the page

This page has been automatically generated. If you have any comments or suggestions about the page layout send a mail to ROOT support, or contact the developers with any questions or problems regarding ROOT.