// @(#)root/graf:$Id$ // Author: Otto Schaile 20/11/99 /************************************************************************* * 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. * *************************************************************************/ /** \class TCurlyLine \ingroup BasicGraphics Implements curly or wavy polylines used to draw Feynman diagrams. Amplitudes and wavelengths may be specified in the constructors, via commands or interactively from popup menus. The class make use of TPolyLine by inheritance, ExecuteEvent methods are highly inspired from the methods used in TPolyLine and TArc. The picture below has been generated by the tutorial feynman. Begin_Macro(source) ../../../tutorials/graphics/feynman.C End_Macro */ #include "Riostream.h" #include "TCurlyLine.h" #include "TROOT.h" #include "TVirtualPad.h" #include "TVirtualX.h" #include "TMath.h" #include "TLine.h" #include "TPoint.h" Double_t TCurlyLine::fgDefaultWaveLength = 0.02; Double_t TCurlyLine::fgDefaultAmplitude = 0.01; Bool_t TCurlyLine::fgDefaultIsCurly = kTRUE; ClassImp(TCurlyLine); //////////////////////////////////////////////////////////////////////////////// /// Default constructor. TCurlyLine::TCurlyLine() { fX1 = 0.; fY1 = 0.; fX2 = 0.; fY2 = 0.; fWaveLength = 0.; fAmplitude = 0.; fIsCurly = fgDefaultIsCurly; fNsteps = 0; } //////////////////////////////////////////////////////////////////////////////// /// Create a new TCurlyLine with starting point (x1, y1), end point (x2,y2). /// The wavelength and amplitude are given in percent of the pad height. TCurlyLine::TCurlyLine(Double_t x1, Double_t y1, Double_t x2, Double_t y2, Double_t wl, Double_t amp) { fX1 = x1; fY1 = y1; fX2 = x2; fY2 = y2; fWaveLength = wl; fAmplitude = amp; fIsCurly = fgDefaultIsCurly; Build(); } //////////////////////////////////////////////////////////////////////////////// /// Create a curly (Gluon) or wavy (Gamma) line. void TCurlyLine::Build() { Double_t pixeltoX = 1; Double_t pixeltoY = 1; Double_t wavelengthPix,amplitudePix, lengthPix, hPix; Double_t px1, py1, px2, py2; if (gPad) { Double_t ww = (Double_t)gPad->GetWw(); Double_t wh = (Double_t)gPad->GetWh(); Double_t pxrange = gPad->GetAbsWNDC()*ww; Double_t pyrange = - gPad->GetAbsHNDC()*wh; Double_t xrange = gPad->GetX2() - gPad->GetX1(); Double_t yrange = gPad->GetY2() - gPad->GetY1(); pixeltoX = xrange / pxrange; pixeltoY = yrange/pyrange; hPix = TMath::Max(gPad->GetAbsHNDC() * gPad->GetWh(), gPad->GetAbsWNDC() * gPad->GetWw()); px1 = gPad->XtoAbsPixel(fX1); py1 = gPad->YtoAbsPixel(fY1); px2 = gPad->XtoAbsPixel(fX2); py2 = gPad->YtoAbsPixel(fY2); lengthPix = TMath::Sqrt((px2-px1)*(px2-px1) + (py1-py2)*(py1-py2)); wavelengthPix = hPix*fWaveLength; amplitudePix = hPix*fAmplitude; } else { wavelengthPix = fWaveLength; amplitudePix = fAmplitude; px1 = fX1; py1 = fY1; px2 = fX2; py2 = fY2; lengthPix = TMath::Sqrt((px2-px1)*(px2-px1) + (py1-py2)*(py1-py2)); } // construct the curly / wavy line in pixel coordinates at angle 0 Double_t anglestep = 40; Double_t phimaxle = TMath::Pi() * 2. / anglestep ; Double_t dx = wavelengthPix / 40; Double_t len2pi = dx * anglestep; // make sure there is a piece of straight line a both ends Double_t lengthcycle = 0.5 * len2pi + 2 * amplitudePix; // if (fIsCurly) lengthcycle += amplitudePix; Int_t nperiods = (Int_t)((lengthPix - lengthcycle) / len2pi); Double_t restlength = 0.5 * (lengthPix - nperiods * len2pi - lengthcycle); fNsteps = (Int_t)(anglestep * nperiods + anglestep / 2 + 4); if (fNsteps < 2) fNsteps = 2; SetPolyLine(fNsteps); Double_t *xv = GetX(); Double_t *yv = GetY(); xv[0] = 0; yv[0] = 0; xv[1] = restlength; yv[1] = 0; Double_t phase = 1.5 * TMath::Pi(); Double_t x0 = amplitudePix + restlength; Int_t i; for(i = 2; i < fNsteps-1; i++){ // distinguish between curly and wavy if (fIsCurly) xv[i] = x0 + amplitudePix * TMath::Sin(phase); else xv[i] = x0; yv[i] = amplitudePix*TMath::Cos(phase); phase += phimaxle; x0 += dx; } xv[fNsteps-1] = lengthPix; yv[fNsteps-1] = 0; if (InheritsFrom("TCurlyArc")) return; // called by TCurlyArc // rotate object and transform back to user coordinates Double_t angle = TMath::ATan2(py2-py1, px2-px1); if (angle < 0) angle += 2*TMath::Pi(); Double_t cosang = TMath::Cos(angle); Double_t sinang = TMath::Sin(angle); Double_t xx, yy; for(i = 0; i < fNsteps; i++){ xx = xv[i] * cosang - yv[i] * sinang; yy = xv[i] * sinang + yv[i] * cosang; if (gPad) { xx *= pixeltoX; yy *= pixeltoY; } xv[i] = xx + fX1; yv[i] = yy + fY1; } if (gPad) gPad->Modified(); } //////////////////////////////////////////////////////////////////////////////// /// Compute distance from point px,py to a line. Int_t TCurlyLine::DistancetoPrimitive(Int_t px, Int_t py) { return DistancetoLine(px,py,fX1,fY1,fX2,fY2); } //////////////////////////////////////////////////////////////////////////////// /// Execute action corresponding to one event. /// /// This member function is called when a TCurlyLine 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. void TCurlyLine::ExecuteEvent(Int_t event, Int_t px, Int_t py) { if (!gPad) return; Int_t kMaxDiff = 20; static Int_t d1,d2,px1,px2,py1,py2; static Int_t pxold, pyold, px1old, py1old, px2old, py2old; static Bool_t p1, p2, pL; Int_t dx, dy; Bool_t opaque = gPad->OpaqueMoving(); switch (event) { case kArrowKeyPress: case kButton1Down: if (!opaque) { gVirtualX->SetLineColor(-1); TAttLine::Modify(); //Change line attributes only if necessary } // No break !!! case kMouseMotion: px1 = gPad->XtoAbsPixel(fX1); py1 = gPad->YtoAbsPixel(fY1); px2 = gPad->XtoAbsPixel(fX2); py2 = gPad->YtoAbsPixel(fY2); p1 = p2 = pL = kFALSE; d1 = TMath::Abs(px1 - px) + TMath::Abs(py1-py); //simply take sum of pixels differences if (d1 < kMaxDiff) { //*-*================>OK take point number 1 px1old = px1; py1old = py1; p1 = kTRUE; gPad->SetCursor(kPointer); return; } d2 = TMath::Abs(px2 - px) + TMath::Abs(py2-py); //simply take sum of pixels differences if (d2 < kMaxDiff) { //*-*================>OK take point number 2 px2old = px2; py2old = py2; p2 = kTRUE; gPad->SetCursor(kPointer); return; } pL = kTRUE; pxold = px; pyold = py; gPad->SetCursor(kMove); break; case kArrowKeyRelease: case kButton1Motion: if (p1) { if (!opaque) { gVirtualX->DrawLine(px1old, py1old, px2, py2); gVirtualX->DrawLine(px, py, px2, py2); } else this->SetStartPoint(gPad->AbsPixeltoX(px),gPad->AbsPixeltoY(py)); px1old = px; py1old = py; } if (p2) { if (!opaque) { gVirtualX->DrawLine(px1, py1, px2old, py2old); gVirtualX->DrawLine(px1, py1, px, py); } else this->SetEndPoint(gPad->AbsPixeltoX(px), gPad->AbsPixeltoY(py)); px2old = px; py2old = py; } if (pL) { if (!opaque) gVirtualX->DrawLine(px1, py1, px2, py2); dx = px-pxold; dy = py-pyold; px1 += dx; py1 += dy; px2 += dx; py2 += dy; if (!opaque) gVirtualX->DrawLine(px1, py1, px2, py2); pxold = px; pyold = py; if (opaque) { this->SetStartPoint(gPad->AbsPixeltoX(px1),gPad->AbsPixeltoY(py1)); this->SetEndPoint(gPad->AbsPixeltoX(px2), gPad->AbsPixeltoY(py2)); } } if (opaque) { if (p1) { //check in which corner the BBox is edited if (fX1>fX2) { if (fY1>fY2) gPad->ShowGuidelines(this, event, '2', true); else gPad->ShowGuidelines(this, event, '3', true); } else { if (fY1>fY2) gPad->ShowGuidelines(this, event, '1', true); else gPad->ShowGuidelines(this, event, '4', true); } } if (p2) { //check in which corner the BBox is edited if (fX1>fX2) { if (fY1>fY2) gPad->ShowGuidelines(this, event, '4', true); else gPad->ShowGuidelines(this, event, '1', true); } else { if (fY1>fY2) gPad->ShowGuidelines(this, event, '3', true); else gPad->ShowGuidelines(this, event, '2', true); } } if (pL) { gPad->ShowGuidelines(this, event, 'i', true); } gPad->Modified(kTRUE); gPad->Update(); } break; case kButton1Up: if (opaque) { gPad->ShowGuidelines(this, event); } else { if (p1) { fX1 = gPad->AbsPixeltoX(px); fY1 = gPad->AbsPixeltoY(py); } if (p2) { fX2 = gPad->AbsPixeltoX(px); fY2 = gPad->AbsPixeltoY(py); } if (pL) { fX1 = gPad->AbsPixeltoX(px1); fY1 = gPad->AbsPixeltoY(py1); fX2 = gPad->AbsPixeltoX(px2); fY2 = gPad->AbsPixeltoY(py2); } } Build(); gPad->Modified(); if (!opaque) gVirtualX->SetLineColor(-1); } } //////////////////////////////////////////////////////////////////////////////// /// Save primitive as a C++ statement(s) on output stream out void TCurlyLine::SavePrimitive(std::ostream &out, Option_t * /*= ""*/) { if (gROOT->ClassSaved(TCurlyLine::Class())) { out<<" "; } else { out<<" TCurlyLine *"; } out<<"curlyline = new TCurlyLine(" <SetWavy();"<Draw();"<XtoPixel(fX1); px2 = gPad->XtoPixel(fX2); py1 = gPad->YtoPixel(fY1); py2 = gPad->YtoPixel(fY2); Int_t tmp; if (px1>px2) { tmp = px1; px1 = px2; px2 = tmp;} if (py1>py2) { tmp = py1; py1 = py2; py2 = tmp;} BBox.fX = px1; BBox.fY = py1; BBox.fWidth = px2-px1; BBox.fHeight = py2-py1; return (BBox); } //////////////////////////////////////////////////////////////////////////////// /// Return the center of the BoundingBox as TPoint in pixels TPoint TCurlyLine::GetBBoxCenter() { TPoint p; p.SetX(gPad->XtoPixel(TMath::Min(fX1,fX2)+0.5*(TMath::Max(fX1, fX2)-TMath::Min(fX1, fX2)))); p.SetY(gPad->YtoPixel(TMath::Min(fY1,fY2)+0.5*(TMath::Max(fY1, fY2)-TMath::Min(fY1, fY2)))); return(p); } //////////////////////////////////////////////////////////////////////////////// /// Set center of the BoundingBox void TCurlyLine::SetBBoxCenter(const TPoint &p) { Double_t w = TMath::Max(fX1, fX2)-TMath::Min(fX1, fX2); Double_t h = TMath::Max(fY1, fY2)-TMath::Min(fY1, fY2); Double_t x1, x2, y1, y2; x1 = x2 = y1 = y2 = 0; if (fX2>fX1) { x1 = gPad->PixeltoX(p.GetX())-0.5*w; x2 = gPad->PixeltoX(p.GetX())+0.5*w; } else { x2 = gPad->PixeltoX(p.GetX())-0.5*w; x1 = gPad->PixeltoX(p.GetX())+0.5*w; } if (fY2>fY1) { y1 = gPad->PixeltoY(p.GetY()-gPad->VtoPixel(0))-0.5*h; y2 = gPad->PixeltoY(p.GetY()-gPad->VtoPixel(0))+0.5*h; } else { y2 = gPad->PixeltoY(p.GetY()-gPad->VtoPixel(0))-0.5*h; y1 = gPad->PixeltoY(p.GetY()-gPad->VtoPixel(0))+0.5*h; } this->SetStartPoint(x1, y1); this->SetEndPoint(x2, y2); } //////////////////////////////////////////////////////////////////////////////// /// Set X coordinate of the center of the BoundingBox void TCurlyLine::SetBBoxCenterX(const Int_t x) { Double_t w = TMath::Max(fX1, fX2)-TMath::Min(fX1, fX2); if (fX2>fX1) { this->SetStartPoint(gPad->PixeltoX(x)-0.5*w, fY1); this->SetEndPoint(gPad->PixeltoX(x)+0.5*w, fY2); } else { this->SetEndPoint(gPad->PixeltoX(x)-0.5*w, fY2); this->SetStartPoint(gPad->PixeltoX(x)+0.5*w, fY1); } } //////////////////////////////////////////////////////////////////////////////// /// Set Y coordinate of the center of the BoundingBox void TCurlyLine::SetBBoxCenterY(const Int_t y) { Double_t h = TMath::Max(fY1, fY2)-TMath::Min(fY1, fY2); if (fY2>fY1) { this->SetStartPoint(fX1, gPad->PixeltoY(y-gPad->VtoPixel(0))-0.5*h); this->SetEndPoint(fX2, gPad->PixeltoY(y-gPad->VtoPixel(0))+0.5*h); } else { this->SetEndPoint(fX2, gPad->PixeltoY(y-gPad->VtoPixel(0))-0.5*h); this->SetStartPoint(fX1, gPad->PixeltoY(y-gPad->VtoPixel(0))+0.5*h); } } //////////////////////////////////////////////////////////////////////////////// /// Set left hand side of BoundingBox to a value /// (resize in x direction on left) void TCurlyLine::SetBBoxX1(const Int_t x) { if (fX2>fX1) this->SetStartPoint(gPad->PixeltoX(x), fY1); else this->SetEndPoint(gPad->PixeltoX(x), fY2); } //////////////////////////////////////////////////////////////////////////////// /// Set right hands ide of BoundingBox to a value /// (resize in x direction on right) void TCurlyLine::SetBBoxX2(const Int_t x) { if (fX2>fX1) this->SetEndPoint(gPad->PixeltoX(x), fY2); else this->SetStartPoint(gPad->PixeltoX(x), fY1); } //////////////////////////////////////////////////////////////////////////////// /// Set top of BoundingBox to a value (resize in y direction on top) void TCurlyLine::SetBBoxY1(const Int_t y) { if (fY2>fY1) this->SetEndPoint(fX2, gPad->PixeltoY(y - gPad->VtoPixel(0))); else this->SetStartPoint(fX1, gPad->PixeltoY(y - gPad->VtoPixel(0))); } //////////////////////////////////////////////////////////////////////////////// /// Set bottom of BoundingBox to a value /// (resize in y direction on bottom) void TCurlyLine::SetBBoxY2(const Int_t y) { if (fY2>fY1) this->SetStartPoint(fX1, gPad->PixeltoY(y - gPad->VtoPixel(0))); else this->SetEndPoint(fX2, gPad->PixeltoY(y - gPad->VtoPixel(0))); }