TGLRotateManip.cxx

Go to the documentation of this file.
00001 // @(#)root/gl:$Id: TGLRotateManip.cxx 34006 2010-06-21 10:36:05Z matevz $
00002 // Author:  Richard Maunder  04/10/2005
00003 
00004 /*************************************************************************
00005  * Copyright (C) 1995-2005, Rene Brun and Fons Rademakers.               *
00006  * All rights reserved.                                                  *
00007  *                                                                       *
00008  * For the licensing terms see $ROOTSYS/LICENSE.                         *
00009  * For the list of contributors see $ROOTSYS/README/CREDITS.             *
00010  *************************************************************************/
00011 
00012 #include "TGLRotateManip.h"
00013 #include "TGLPhysicalShape.h"
00014 #include "TGLCamera.h"
00015 #include "TGLIncludes.h"
00016 #include "TMath.h"
00017 #include "TError.h"
00018 
00019 //______________________________________________________________________________
00020 //                                                                      
00021 // Rotate manipulator - attaches to physical shape and draws local axes 
00022 // widgets - rings drawn from attached physical center, in plane defined
00023 // by axis. User can mouse over (turns yellow) and L click/drag to      
00024 // rotate attached physical round the ring center.                      
00025 // Widgets use standard 3D package axes colours: X red, Y green, Z blue.
00026 
00027 ClassImp(TGLRotateManip);
00028 
00029 //______________________________________________________________________________
00030 Double_t TGLRotateManip::Angle(const TGLVector3& v1, const TGLVector3& v2)
00031 {
00032    // Calculate unsigned angle between vectors v1 and v2
00033    return TMath::ACos(Dot(v1, v2) / (v1.Mag() * v2.Mag()));
00034 }
00035 
00036 //______________________________________________________________________________
00037 Double_t TGLRotateManip::Angle(const TGLVector3& v1, const TGLVector3& v2,
00038                                const TGLVector3& ref)
00039 {
00040    // Calculate signed angle between vectors v1 and v2, using ref to define right handed coord system
00041    // If v1.v2 parallel to ref vector: +ive for clockwise, -ive for anticlockwise
00042    // If v1.v2 antiparallel to ref vector: -ive for clockwise, +ive for anticlockwise
00043    TGLVector3 cross = Cross(v1, v2);
00044    if (Dot(cross,ref) > 0.0) {
00045       return Angle(v1, v2);
00046    } else {
00047       return -Angle(v1, v2);
00048    }
00049 }
00050 
00051 //______________________________________________________________________________
00052 TGLRotateManip::TGLRotateManip() :
00053    fShallowRing(kFALSE), fShallowFront(kTRUE),
00054    fActiveRingPlane(TGLVector3(1.0, 0.0, 0.0), TGLVertex3(0.0, 0.0, 0.0)),
00055    fActiveRingCenter(TGLVertex3(0.0, 0.0, 0.0)),
00056    fRingLine(TGLVertex3(0.0, 0.0, 0.0), TGLVertex3(0.0, 0.0, 0.0)),
00057    fRingLineOld(TGLVertex3(0.0, 0.0, 0.0), TGLVertex3(0.0, 0.0, 0.0))
00058 {
00059    // Construct rotation manipulator not bound to any physical shape.
00060 }
00061 
00062 //______________________________________________________________________________
00063 TGLRotateManip::TGLRotateManip(TGLPhysicalShape* shape) :
00064    TGLManip(shape),
00065    fShallowRing(kFALSE), fShallowFront(kTRUE),
00066    fActiveRingPlane(TGLVector3(1.0, 0.0, 0.0), TGLVertex3(0.0, 0.0, 0.0)),
00067    fActiveRingCenter(TGLVertex3(0.0, 0.0, 0.0)),
00068    fRingLine(TGLVertex3(0.0, 0.0, 0.0), TGLVertex3(0.0, 0.0, 0.0)),
00069    fRingLineOld(TGLVertex3(0.0, 0.0, 0.0), TGLVertex3(0.0, 0.0, 0.0))
00070 {
00071    // Construct rotation manipulator bound to TGLPhysicalShape 'shape'.
00072 }
00073 
00074 //______________________________________________________________________________
00075 TGLRotateManip::~TGLRotateManip()
00076 {
00077    // Destory the rotation manipulator
00078 }
00079 
00080 //______________________________________________________________________________
00081 void TGLRotateManip::Draw(const TGLCamera& camera) const
00082 {
00083    // Draw rotate manipulator - axis rings drawn from attached
00084    // physical center, in plane defined by axis as normal, in red(X),
00085    // green(Y) and blue(Z), with white center sphere. If selected
00086    // widget (mouse over) this is drawn in active colour (yellow).
00087 
00088    if (!fShape) {
00089       return;
00090    }
00091 
00092    // Get draw scales
00093    const TGLBoundingBox& box = fShape->BoundingBox();
00094    Double_t baseScale;
00095    TGLVector3 axisScale[3];
00096    CalcDrawScale(box, camera, baseScale, axisScale);
00097    Double_t ringRadius = baseScale*10.0;
00098 
00099    // Get permitted manipulations on shape
00100    TGLPhysicalShape::EManip manip = fShape->GetManip();
00101 
00102    glEnable(GL_BLEND);
00103    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
00104    glDisable(GL_CULL_FACE);
00105 
00106    TGLUtil::TDrawQualityScaler hiRes(3);
00107 
00108    // Draw three axis rings where permitted
00109    // Not drawing will prevent interaction
00110    // GL name loading for hit testing - 0 reserved for no selection
00111    if (manip & TGLPhysicalShape::kRotateX) {
00112       glPushName(1);
00113       TGLUtil::DrawRing(box.Center(), box.Axis(0, kTRUE), ringRadius*1.004, ColorFor(1));
00114       glPopName();
00115    } else {
00116       TGLUtil::DrawRing(box.Center(), box.Axis(0, kTRUE), ringRadius*1.004, TGLUtil::fgGrey);
00117    }
00118    if (manip & TGLPhysicalShape::kRotateY) {
00119       glPushName(2);
00120       TGLUtil::DrawRing(box.Center(), box.Axis(1, kTRUE), ringRadius*1.002, ColorFor(2));
00121       glPopName();
00122    } else {
00123       TGLUtil::DrawRing(box.Center(), box.Axis(1, kTRUE), ringRadius*1.002, TGLUtil::fgGrey);
00124    }
00125    if (manip & TGLPhysicalShape::kRotateZ) {
00126       glPushName(3);
00127       TGLUtil::DrawRing(box.Center(), box.Axis(2, kTRUE), ringRadius, ColorFor(3));
00128       glPopName();
00129    } else {
00130       TGLUtil::DrawRing(box.Center(), box.Axis(2, kTRUE), ringRadius, TGLUtil::fgGrey);
00131    }
00132    // Draw white center sphere
00133    TGLUtil::DrawSphere(box.Center(), ringRadius/20.0, TGLUtil::fgWhite);
00134 
00135    // Indicate we are in ring follow (non-shallow) mode
00136    // by drawing line from center to dragged ring point
00137    if (fActive) {
00138       if (fShallowRing) {
00139          TGLVertex3 eyeOnRing;
00140          if (fShallowFront) {
00141             eyeOnRing = fActiveRingCenter - (camera.EyeDirection()*ringRadius);
00142          } else {
00143             eyeOnRing = fActiveRingCenter + (camera.EyeDirection()*ringRadius);
00144          }
00145 
00146          eyeOnRing = fActiveRingPlane.NearestOn(eyeOnRing);
00147          TGLVector3 arrowDir = Cross(fActiveRingPlane.Norm(), eyeOnRing - fActiveRingCenter);
00148          arrowDir.Normalise();
00149          TGLUtil::DrawLine(eyeOnRing,  arrowDir*ringRadius*1.3, TGLUtil::kLineHeadArrow, baseScale, TGLUtil::fgYellow);
00150          TGLUtil::DrawLine(eyeOnRing, -arrowDir*ringRadius*1.3, TGLUtil::kLineHeadArrow, baseScale, TGLUtil::fgYellow);
00151       } else {
00152          TGLVector3 activeVector = fRingLine.Vector();
00153          activeVector.Normalise();
00154          activeVector *= ringRadius;
00155          TGLUtil::DrawLine(fRingLine.Start(), activeVector,
00156                            TGLUtil::kLineHeadNone, baseScale, TGLUtil::fgYellow);
00157       }
00158    }
00159 
00160    glEnable(GL_CULL_FACE);
00161    glDisable(GL_BLEND);
00162 }
00163 
00164 //______________________________________________________________________________
00165 Bool_t TGLRotateManip::HandleButton(const Event_t& event, const TGLCamera& camera)
00166 {
00167    // Handle mouse button event over manipulator - returns kTRUE if
00168    // redraw required kFALSE otherwise.
00169 
00170    Bool_t captured = TGLManip::HandleButton(event, camera);
00171 
00172    if (captured) {
00173       // Find active selected axis
00174       UInt_t axisIndex = fSelectedWidget - 1; // Ugg sort out axis / widget id mapping
00175       TGLVector3 widgetAxis = fShape->BoundingBox().Axis(axisIndex, kTRUE);
00176 
00177       // Construct plane for the axis ring, using normal and center point
00178       fActiveRingPlane.Set(widgetAxis, fShape->BoundingBox().Center());
00179       fActiveRingCenter.Set(fShape->BoundingBox().Center());
00180 
00181       fRingLineOld = fRingLine = CalculateRingLine(fLastMouse, camera);
00182 
00183       // Is plane at shallow angle to eye line if angle between normal of plane and
00184       // eye line is ~90 deg (PI/4)
00185       Double_t planeEyeAngle = Angle(fActiveRingPlane.Norm(), camera.EyeDirection()) - TMath::ASin(1.0);
00186       Double_t shallowDelta = 0.15;
00187       if ((planeEyeAngle > -shallowDelta) && (planeEyeAngle < shallowDelta)) {
00188          fShallowRing = kTRUE;
00189 
00190          // Work out ring follow direction - if clicked on back or front of ring.
00191          // If plane/eye angle very shallow force to front
00192 
00193                         /* DISABLED - Force onto front always */
00194                         fShallowFront = kTRUE;
00195                         /*
00196          if ((planeEyeAngle > -shallowDelta/3.0) && (planeEyeAngle < shallowDelta/3.0) ||
00197              Dot(fRingLine.Vector(), camera.FrustumPlane(TGLCamera::kNear).Norm()) < 0.0) {
00198             fShallowFront = kTRUE;
00199          } else {
00200             fShallowFront = kFALSE;
00201          }*/
00202       } else {
00203          fShallowRing = kFALSE;
00204       }
00205    }
00206 
00207    return captured;
00208 }
00209 
00210 //______________________________________________________________________________
00211 Bool_t TGLRotateManip::HandleMotion(const Event_t& event, const TGLCamera& camera)
00212 {
00213    // Handle mouse motion over manipulator - if active (selected
00214    // widget) rotate physical around selected ring widget plane
00215    // normal. Returns kTRUE if redraw required kFALSE otherwise.
00216 
00217    if (fActive) {
00218       TPoint newMouse(event.fX, event.fY);
00219 
00220       // Calculate singed angle delta between old and new ring position using
00221       Double_t angle = CalculateAngleDelta(newMouse, camera);
00222       fShape->Rotate(fActiveRingCenter, fActiveRingPlane.Norm(), angle);
00223       fLastMouse = newMouse;
00224       return kTRUE;
00225    }
00226    return kFALSE;
00227 }
00228 
00229 //______________________________________________________________________________
00230 Double_t TGLRotateManip::CalculateAngleDelta(const TPoint& mouse, const TGLCamera& camera)
00231 {
00232    // Calculate angle delta for rotation based on new mouse position.
00233 
00234    if (fShallowRing) {
00235       std::pair<Bool_t, TGLLine3> nearLineIntersection = Intersection(fActiveRingPlane,
00236                                                                       camera.FrustumPlane(TGLCamera::kNear));
00237       if (!nearLineIntersection.first) {
00238          Error("TGLRotateManip::CalculateAngleDelta", "active ring plane parallel to near clip?");
00239          return 1.0;
00240       }
00241       TGLLine3 nearLine = nearLineIntersection.second;
00242       TGLVector3 activePlaneNear = camera.WorldDeltaToViewport(nearLine.Start(), nearLine.Vector());
00243       activePlaneNear.Normalise();
00244       TGLVector3 mouseDelta(mouse.GetX() - fLastMouse.GetX(),
00245                             -(mouse.GetY() - fLastMouse.GetY()),
00246                             0.0);
00247 
00248       Double_t angle = Dot(activePlaneNear, mouseDelta) / 150.0;
00249       if (fShallowFront) {
00250          return -angle;
00251       } else {
00252          return angle;
00253       }
00254    } else {
00255       fRingLineOld = fRingLine;
00256       fRingLine = CalculateRingLine(fLastMouse, camera);
00257       return Angle(fRingLineOld.Vector(), fRingLine.Vector(), fActiveRingPlane.Norm());
00258    }
00259 }
00260 
00261 //______________________________________________________________________________
00262 TGLLine3 TGLRotateManip::CalculateRingLine(const TPoint& mouse, const TGLCamera& camera) const
00263 {
00264    // Calculated interaction line between 'mouse' viewport point, and
00265    // current selected widget (ring), under supplied 'camera'
00266    // projection.
00267 
00268    // Find mouse position in viewport coords
00269    TPoint mouseViewport(mouse);
00270    camera.WindowToViewport(mouseViewport);
00271 
00272    // Find projection of mouse into world
00273    TGLLine3 viewportProjection = camera.ViewportToWorld(mouseViewport);
00274 
00275    // Find rotation line from ring center to this intersection on plane
00276    std::pair<Bool_t, TGLVertex3> ringPlaneInter =  Intersection(fActiveRingPlane, viewportProjection, kTRUE);
00277 
00278    // If intersection fails then ring is parallel to eye line - in this case
00279    // force line to run from center back towards viewer (opposite eye line)
00280    if (!ringPlaneInter.first) {
00281       return TGLLine3(fActiveRingCenter, -camera.EyeDirection());
00282    }
00283    return TGLLine3(fActiveRingCenter, ringPlaneInter.second);
00284 }
00285 

Generated on Tue Jul 5 14:18:08 2011 for ROOT_528-00b_version by  doxygen 1.5.1