// @(#)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. *
*************************************************************************/
//______________________________________________________________________________
//
// This class implements curly or wavy polylines typically 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_Html
/*
*/
//End_Html
//______________________________________________________________________________
#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)
//______________________________________________________________________________
TCurlyLine::TCurlyLine()
{
// Default constructor.
fX1 = 0.;
fY1 = 0.;
fX2 = 0.;
fY2 = 0.;
fWaveLength = 0.;
fAmplitude = 0.;
fIsCurly = fgDefaultIsCurly;
fNsteps = 0;
}
//______________________________________________________________________________
TCurlyLine::TCurlyLine(Double_t x1, Double_t y1, Double_t x2, Double_t y2, Double_t wl, Double_t amp)
{
// 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.
fX1 = x1;
fY1 = y1;
fX2 = x2;
fY2 = y2;
fWaveLength = wl;
fAmplitude = amp;
fIsCurly = fgDefaultIsCurly;
Build();
}
//______________________________________________________________________________
void TCurlyLine::Build()
{
// Create a curly (Gluon) or wavy (Gamma) line.
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 < 1) fNsteps = 1;
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();
}
//______________________________________________________________________________
Int_t TCurlyLine::DistancetoPrimitive(Int_t px, Int_t py)
{
// Compute distance from point px,py to a line.
return DistancetoLine(px,py,fX1,fY1,fX2,fY2);
}
//______________________________________________________________________________
void TCurlyLine::ExecuteEvent(Int_t event, Int_t px, Int_t py)
{
// 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.
//
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 eddited
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 eddited
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);
}
}
//______________________________________________________________________________
void TCurlyLine::SavePrimitive(ostream &out, Option_t * /*= ""*/)
{
// Save primitive as a C++ statement(s) on output stream out
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);
}
//______________________________________________________________________________
TPoint TCurlyLine::GetBBoxCenter()
{
// Return the center of the BoundingBox as TPoint in pixels
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);
}
//______________________________________________________________________________
void TCurlyLine::SetBBoxCenter(const TPoint &p)
{
// Set center of the BoundingBox
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);
}
//______________________________________________________________________________
void TCurlyLine::SetBBoxCenterX(const Int_t x)
{
// Set X coordinate of the center of the BoundingBox
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);
}
}
//______________________________________________________________________________
void TCurlyLine::SetBBoxCenterY(const Int_t y)
{
// Set Y coordinate of the center of the BoundingBox
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);
}
}
//______________________________________________________________________________
void TCurlyLine::SetBBoxX1(const Int_t x)
{
// Set lefthandside of BoundingBox to a value
// (resize in x direction on left)
if (fX2>fX1)
this->SetStartPoint(gPad->PixeltoX(x), fY1);
else
this->SetEndPoint(gPad->PixeltoX(x), fY2);
}
//______________________________________________________________________________
void TCurlyLine::SetBBoxX2(const Int_t x)
{
// Set righthandside of BoundingBox to a value
// (resize in x direction on right)
if (fX2>fX1)
this->SetEndPoint(gPad->PixeltoX(x), fY2);
else
this->SetStartPoint(gPad->PixeltoX(x), fY1);
}
//_______________________________________________________________________________
void TCurlyLine::SetBBoxY1(const Int_t y)
{
// Set top of BoundingBox to a value (resize in y direction on top)
if (fY2>fY1)
this->SetEndPoint(fX2, gPad->PixeltoY(y - gPad->VtoPixel(0)));
else
this->SetStartPoint(fX1, gPad->PixeltoY(y - gPad->VtoPixel(0)));
}
//_______________________________________________________________________________
void TCurlyLine::SetBBoxY2(const Int_t y)
{
// Set bottom of BoundingBox to a value
// (resize in y direction on bottom)
if (fY2>fY1)
this->SetStartPoint(fX1, gPad->PixeltoY(y - gPad->VtoPixel(0)));
else
this->SetEndPoint(fX2, gPad->PixeltoY(y - gPad->VtoPixel(0)));
}