// @(#)root/graf:$Id$ // Author: Rene Brun 17/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 "Riostream.h" #include "TROOT.h" #include "TMath.h" #include "TArrow.h" #include "TVirtualPad.h" #include "TVirtualPS.h" #include "TVirtualX.h" Float_t TArrow::fgDefaultAngle = 60; Float_t TArrow::fgDefaultArrowSize = 0.05; TString TArrow::fgDefaultOption = ">"; ClassImp(TArrow); /** \class TArrow \ingroup BasicGraphics Draw all kinds of Arrows. The different arrow's formats are explained in TArrow::TArrow. The picture below gives some examples. Once an arrow is drawn on the screen: - One can click on one of the edges and move this edge. - One can click on any other arrow part to move the entire arrow. Begin_Macro(source) ../../../tutorials/graphics/arrows.C End_Macro */ //////////////////////////////////////////////////////////////////////////////// /// Arrow default constructor. TArrow::TArrow(): TLine(),TAttFill() { fAngle = fgDefaultAngle; fArrowSize = 0.; } //////////////////////////////////////////////////////////////////////////////// /// Arrow normal constructor. /// /// Define an arrow between points x1,y1 and x2,y2 /// the `arrowsize` is in percentage of the pad height /// Opening angle between the two sides of the arrow is fAngle (60 degrees) /// ~~~ {.cpp} /// option = ">" --------> /// option = "|->" |-------> /// option = "<" <-------- /// option = "<-|" <-------| /// option = "->-" ---->---- /// option = "-<-" ----<---- /// option = "-|>-" ---|>---- /// option = "<>" <-------> /// option = "<|>" <|-----|> arrow defined by a triangle /// ~~~ /// Note: /// /// - If FillColor == 0 an open triangle is drawn, otherwise a full triangle is /// drawn with the fill color. The default is filled with LineColor /// - The "Begin" and "end" bars options can be combined with any other options. /// - The 9 options described above cannot be mixed. TArrow::TArrow(Double_t x1, Double_t y1,Double_t x2, Double_t y2, Float_t arrowsize ,Option_t *option) :TLine(x1,y1,x2,y2), TAttFill(0,1001) { fAngle = fgDefaultAngle; fArrowSize = arrowsize; fOption = option; SetFillColor(GetLineColor()); } //////////////////////////////////////////////////////////////////////////////// /// Arrow default destructor. TArrow::~TArrow() { } //////////////////////////////////////////////////////////////////////////////// /// Copy constructor. TArrow::TArrow(const TArrow &arrow) : TLine(), TAttFill() { fAngle = fgDefaultAngle; fArrowSize = 0.; arrow.Copy(*this); } //////////////////////////////////////////////////////////////////////////////// /// Copy this arrow to arrow void TArrow::Copy(TObject &obj) const { TLine::Copy(obj); TAttFill::Copy(((TArrow&)obj)); ((TArrow&)obj).fAngle = fAngle; ((TArrow&)obj).fArrowSize = fArrowSize; ((TArrow&)obj).fOption = fOption; } //////////////////////////////////////////////////////////////////////////////// /// Draw this arrow with its current attributes. void TArrow::Draw(Option_t *option) { Option_t *opt; if (option && strlen(option)) opt = option; else opt = (char*)GetOption(); AppendPad(opt); } //////////////////////////////////////////////////////////////////////////////// /// Draw this arrow with new coordinates. /// /// - if `arrowsize` is <= 0, `arrowsize` will be the current arrow size /// - if `option=""`, `option` will be the current arrow option void TArrow::DrawArrow(Double_t x1, Double_t y1,Double_t x2, Double_t y2, Float_t arrowsize ,Option_t *option) { Float_t size = arrowsize; if (size <= 0) size = fArrowSize; if (size <= 0) size = 0.05; const char* opt = option; if (!opt || !opt[0]) opt = fOption.Data(); if (!opt || !opt[0]) opt = "|>"; TArrow *newarrow = new TArrow(x1,y1,x2,y2,size,opt); newarrow->SetAngle(fAngle); TAttLine::Copy(*newarrow); TAttFill::Copy(*newarrow); newarrow->SetBit(kCanDelete); newarrow->AppendPad(opt); } //////////////////////////////////////////////////////////////////////////////// /// Paint this arrow with its current attributes. void TArrow::Paint(Option_t *option) { Option_t *opt; if (option && strlen(option)) opt = option; else opt = (char*)GetOption(); if (TestBit(kLineNDC)) PaintArrow(gPad->GetX1() + fX1 * (gPad->GetX2() - gPad->GetX1()), gPad->GetY1() + fY1 * (gPad->GetY2() - gPad->GetY1()), gPad->GetX1() + fX2 * (gPad->GetX2() - gPad->GetX1()), gPad->GetY1() + fY2 * (gPad->GetY2() - gPad->GetY1()), fArrowSize, opt); else PaintArrow(gPad->XtoPad(fX1), gPad->YtoPad(fY1), gPad->XtoPad(fX2), gPad->YtoPad(fY2), fArrowSize, opt); } //////////////////////////////////////////////////////////////////////////////// /// Draw this arrow void TArrow::PaintArrow(Double_t x1, Double_t y1, Double_t x2, Double_t y2, Float_t arrowsize, Option_t *option) { // Option and attributes TString opt = option; opt.ToLower(); TAttLine::Modify(); TAttFill::Modify(); // Compute the gPad coordinates in TRUE normalized space (NDC) Int_t iw = gPad->GetWw(); Int_t ih = gPad->GetWh(); Double_t x1p,y1p,x2p,y2p; gPad->GetPadPar(x1p,y1p,x2p,y2p); Int_t ix1 = (Int_t)(iw*x1p); Int_t iy1 = (Int_t)(ih*y1p); Int_t ix2 = (Int_t)(iw*x2p); Int_t iy2 = (Int_t)(ih*y2p); Double_t wndc = TMath::Min(1.,(Double_t)iw/(Double_t)ih); Double_t hndc = TMath::Min(1.,(Double_t)ih/(Double_t)iw); Double_t rh = hndc/(Double_t)ih; Double_t rw = wndc/(Double_t)iw; Double_t x1ndc = (Double_t)ix1*rw; Double_t y1ndc = (Double_t)iy1*rh; Double_t x2ndc = (Double_t)ix2*rw; Double_t y2ndc = (Double_t)iy2*rh; // Ratios to convert user space in TRUE normalized space (NDC) Double_t rx1,ry1,rx2,ry2; gPad->GetRange(rx1,ry1,rx2,ry2); Double_t rx = (x2ndc-x1ndc)/(rx2-rx1); Double_t ry = (y2ndc-y1ndc)/(ry2-ry1); // Arrow position and arrow's middle in NDC space Double_t x1n = rx*(x1-rx1)+x1ndc; Double_t x2n = rx*(x2-rx1)+x1ndc; Double_t y1n = ry*(y1-ry1)+y1ndc; Double_t y2n = ry*(y2-ry1)+y1ndc; Double_t xm = (x1n+x2n)/2; Double_t ym = (y1n+y2n)/2; // Arrow heads size Double_t length = TMath::Sqrt(Double_t((x2n-x1n)*(x2n-x1n)+(y2n-y1n)*(y2n-y1n))); Double_t rSize = 0.7*arrowsize; Double_t dSize = rSize*TMath::Tan(TMath::Pi()*fAngle/360); Double_t cosT = (length > 0) ? (x2n-x1n)/length : 1.; Double_t sinT = (length > 0) ? (y2n-y1n)/length : 0.; // Draw the start and end bars if needed if (opt.BeginsWith("|-")) { Double_t x1ar[2], y1ar[2]; x1ar[0] = x1n-sinT*dSize; y1ar[0] = y1n+cosT*dSize; x1ar[1] = x1n+sinT*dSize; y1ar[1] = y1n-cosT*dSize; // NDC to user coordinates for (Int_t i=0; i<2; i++) { x1ar[i] = (1/rx)*(x1ar[i]-x1ndc)+rx1; y1ar[i] = (1/ry)*(y1ar[i]-y1ndc)+ry1; } gPad->PaintLine(x1ar[0],y1ar[0],x1ar[1],y1ar[1]); opt(0) = ' '; } if (opt.EndsWith("-|")) { Double_t x2ar[2], y2ar[2]; x2ar[0] = x2n-sinT*dSize; y2ar[0] = y2n+cosT*dSize; x2ar[1] = x2n+sinT*dSize; y2ar[1] = y2n-cosT*dSize; // NDC to user coordinates for (Int_t i=0; i<2; i++) { x2ar[i] = (1/rx)*(x2ar[i]-x1ndc)+rx1; y2ar[i] = (1/ry)*(y2ar[i]-y1ndc)+ry1; } gPad->PaintLine(x2ar[0],y2ar[0],x2ar[1],y2ar[1]); opt(opt.Length()-1) = ' '; } // Move arrow head's position if needed Double_t x1h = x1n; Double_t y1h = y1n; Double_t x2h = x2n; Double_t y2h = y2n; if (opt.Contains("->-") || opt.Contains("-|>-")) { x2h = xm + cosT*rSize/2; y2h = ym + sinT*rSize/2; } if (opt.Contains("-<-") || opt.Contains("-<|-")) { x1h = xm - cosT*rSize/2; y1h = ym - sinT*rSize/2; } // Paint Arrow body if (opt.Contains("|>") && !opt.Contains("-|>-")) { x2n -= cosT*rSize; y2n -= sinT*rSize; } if (opt.Contains("<|") && !opt.Contains("-<|-")) { x1n += cosT*rSize; y1n += sinT*rSize; } x1n = (1/rx)*(x1n-x1ndc)+rx1; y1n = (1/ry)*(y1n-y1ndc)+ry1; x2n = (1/rx)*(x2n-x1ndc)+rx1; y2n = (1/ry)*(y2n-y1ndc)+ry1; gPad->PaintLine(x1n,y1n,x2n,y2n); // Draw the arrow's head(s) if (opt.Contains(">")) { Double_t x2ar[4], y2ar[4]; x2ar[0] = x2h - rSize*cosT - sinT*dSize; y2ar[0] = y2h - rSize*sinT + cosT*dSize; x2ar[1] = x2h; y2ar[1] = y2h; x2ar[2] = x2h - rSize*cosT + sinT*dSize; y2ar[2] = y2h - rSize*sinT - cosT*dSize; x2ar[3] = x2ar[0]; y2ar[3] = y2ar[0]; // NDC to user coordinates for (Int_t i=0; i<4; i++) { x2ar[i] = (1/rx)*(x2ar[i]-x1ndc)+rx1; y2ar[i] = (1/ry)*(y2ar[i]-y1ndc)+ry1; } if (opt.Contains("|>")) { if (gVirtualX) gVirtualX->SetLineStyle(1); if (gVirtualPS) gVirtualPS->SetLineStyle(1); if (GetFillColor()) { gPad->PaintFillArea(3,x2ar,y2ar); gPad->PaintPolyLine(4,x2ar,y2ar); } else { gPad->PaintPolyLine(4,x2ar,y2ar); } } else { gPad->PaintPolyLine(3,x2ar,y2ar); } } if (opt.Contains("<")) { Double_t x1ar[4], y1ar[4]; x1ar[0] = x1h + rSize*cosT + sinT*dSize; y1ar[0] = y1h + rSize*sinT - cosT*dSize; x1ar[1] = x1h; y1ar[1] = y1h; x1ar[2] = x1h + rSize*cosT - sinT*dSize; y1ar[2] = y1h + rSize*sinT + cosT*dSize; x1ar[3] = x1ar[0]; y1ar[3] = y1ar[0]; // NDC to user coordinates for (Int_t i=0; i<4; i++) { x1ar[i] = (1/rx)*(x1ar[i]-x1ndc)+rx1; y1ar[i] = (1/ry)*(y1ar[i]-y1ndc)+ry1; } if (opt.Contains("<|")) { if (gVirtualX) gVirtualX->SetLineStyle(1); if (gVirtualPS) gVirtualPS->SetLineStyle(1); if (GetFillColor()) { gPad->PaintFillArea(3,x1ar,y1ar); gPad->PaintPolyLine(4,x1ar,y1ar); } else { gPad->PaintPolyLine(4,x1ar,y1ar); } } else { gPad->PaintPolyLine(3,x1ar,y1ar); } } } //////////////////////////////////////////////////////////////////////////////// /// Save primitive as a C++ statement(s) on output stream out void TArrow::SavePrimitive(std::ostream &out, Option_t * /*= ""*/) { char quote = '"'; if (gROOT->ClassSaved(TArrow::Class())) { out<<" "; } else { out<<" TArrow *"; } out<<"arrow = new TArrow("<SetNDC();"<SetAngle(" << GetAngle() << ");" << std::endl; out<<" arrow->Draw();"<