Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
TGLRotateManip.cxx
Go to the documentation of this file.
1 // @(#)root/gl:$Id$
2 // Author: Richard Maunder 04/10/2005
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2005, Rene Brun and Fons Rademakers. *
6  * All rights reserved. *
7  * *
8  * For the licensing terms see $ROOTSYS/LICENSE. *
9  * For the list of contributors see $ROOTSYS/README/CREDITS. *
10  *************************************************************************/
11 
12 #include "TGLRotateManip.h"
13 #include "TGLPhysicalShape.h"
14 #include "TGLCamera.h"
15 #include "TGLIncludes.h"
16 #include "TMath.h"
17 #include "TError.h"
18 
19 /** \class TGLRotateManip
20 \ingroup opengl
21 Rotate manipulator - attaches to physical shape and draws local axes
22 widgets - rings drawn from attached physical center, in plane defined
23 by axis. User can mouse over (turns yellow) and L click/drag to
24 rotate attached physical round the ring center.
25 Widgets use standard 3D package axes colours: X red, Y green, Z blue.
26 */
27 
28 ClassImp(TGLRotateManip);
29 
30 ////////////////////////////////////////////////////////////////////////////////
31 /// Calculate unsigned angle between vectors v1 and v2
32 
33 Double_t TGLRotateManip::Angle(const TGLVector3& v1, const TGLVector3& v2)
34 {
35  return TMath::ACos(Dot(v1, v2) / (v1.Mag() * v2.Mag()));
36 }
37 
38 ////////////////////////////////////////////////////////////////////////////////
39 /// Calculate signed angle between vectors v1 and v2, using ref to define right handed coord system
40 /// - If v1.v2 parallel to ref vector: +ive for clockwise, -ive for anticlockwise
41 /// - If v1.v2 antiparallel to ref vector: -ive for clockwise, +ive for anticlockwise
42 
43 Double_t TGLRotateManip::Angle(const TGLVector3& v1, const TGLVector3& v2,
44  const TGLVector3& ref)
45 {
46  TGLVector3 cross = Cross(v1, v2);
47  if (Dot(cross,ref) > 0.0) {
48  return Angle(v1, v2);
49  } else {
50  return -Angle(v1, v2);
51  }
52 }
53 
54 ////////////////////////////////////////////////////////////////////////////////
55 /// Construct rotation manipulator not bound to any physical shape.
56 
57 TGLRotateManip::TGLRotateManip() :
58  fShallowRing(kFALSE), fShallowFront(kTRUE),
59  fActiveRingPlane(TGLVector3(1.0, 0.0, 0.0), TGLVertex3(0.0, 0.0, 0.0)),
60  fActiveRingCenter(TGLVertex3(0.0, 0.0, 0.0)),
61  fRingLine(TGLVertex3(0.0, 0.0, 0.0), TGLVertex3(0.0, 0.0, 0.0)),
62  fRingLineOld(TGLVertex3(0.0, 0.0, 0.0), TGLVertex3(0.0, 0.0, 0.0))
63 {
64 }
65 
66 ////////////////////////////////////////////////////////////////////////////////
67 /// Construct rotation manipulator bound to TGLPhysicalShape 'shape'.
68 
69 TGLRotateManip::TGLRotateManip(TGLPhysicalShape* shape) :
70  TGLManip(shape),
71  fShallowRing(kFALSE), fShallowFront(kTRUE),
72  fActiveRingPlane(TGLVector3(1.0, 0.0, 0.0), TGLVertex3(0.0, 0.0, 0.0)),
73  fActiveRingCenter(TGLVertex3(0.0, 0.0, 0.0)),
74  fRingLine(TGLVertex3(0.0, 0.0, 0.0), TGLVertex3(0.0, 0.0, 0.0)),
75  fRingLineOld(TGLVertex3(0.0, 0.0, 0.0), TGLVertex3(0.0, 0.0, 0.0))
76 {
77 }
78 
79 ////////////////////////////////////////////////////////////////////////////////
80 /// Destroy the rotation manipulator
81 
82 TGLRotateManip::~TGLRotateManip()
83 {
84 }
85 
86 ////////////////////////////////////////////////////////////////////////////////
87 /// Draw rotate manipulator - axis rings drawn from attached
88 /// physical center, in plane defined by axis as normal, in red(X),
89 /// green(Y) and blue(Z), with white center sphere. If selected
90 /// widget (mouse over) this is drawn in active colour (yellow).
91 
92 void TGLRotateManip::Draw(const TGLCamera& camera) const
93 {
94  if (!fShape) {
95  return;
96  }
97 
98  // Get draw scales
99  const TGLBoundingBox& box = fShape->BoundingBox();
100  Double_t baseScale;
101  TGLVector3 axisScale[3];
102  CalcDrawScale(box, camera, baseScale, axisScale);
103  Double_t ringRadius = baseScale*10.0;
104 
105  // Get permitted manipulations on shape
106  TGLPhysicalShape::EManip manip = fShape->GetManip();
107 
108  glEnable(GL_BLEND);
109  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
110  glDisable(GL_CULL_FACE);
111 
112  TGLUtil::TDrawQualityScaler hiRes(3);
113 
114  // Draw three axis rings where permitted
115  // Not drawing will prevent interaction
116  // GL name loading for hit testing - 0 reserved for no selection
117  if (manip & TGLPhysicalShape::kRotateX) {
118  glPushName(1);
119  TGLUtil::DrawRing(box.Center(), box.Axis(0, kTRUE), ringRadius*1.004, ColorFor(1));
120  glPopName();
121  } else {
122  TGLUtil::DrawRing(box.Center(), box.Axis(0, kTRUE), ringRadius*1.004, TGLUtil::fgGrey);
123  }
124  if (manip & TGLPhysicalShape::kRotateY) {
125  glPushName(2);
126  TGLUtil::DrawRing(box.Center(), box.Axis(1, kTRUE), ringRadius*1.002, ColorFor(2));
127  glPopName();
128  } else {
129  TGLUtil::DrawRing(box.Center(), box.Axis(1, kTRUE), ringRadius*1.002, TGLUtil::fgGrey);
130  }
131  if (manip & TGLPhysicalShape::kRotateZ) {
132  glPushName(3);
133  TGLUtil::DrawRing(box.Center(), box.Axis(2, kTRUE), ringRadius, ColorFor(3));
134  glPopName();
135  } else {
136  TGLUtil::DrawRing(box.Center(), box.Axis(2, kTRUE), ringRadius, TGLUtil::fgGrey);
137  }
138  // Draw white center sphere
139  TGLUtil::DrawSphere(box.Center(), ringRadius/20.0, TGLUtil::fgWhite);
140 
141  // Indicate we are in ring follow (non-shallow) mode
142  // by drawing line from center to dragged ring point
143  if (fActive) {
144  if (fShallowRing) {
145  TGLVertex3 eyeOnRing;
146  if (fShallowFront) {
147  eyeOnRing = fActiveRingCenter - (camera.EyeDirection()*ringRadius);
148  } else {
149  eyeOnRing = fActiveRingCenter + (camera.EyeDirection()*ringRadius);
150  }
151 
152  eyeOnRing = fActiveRingPlane.NearestOn(eyeOnRing);
153  TGLVector3 arrowDir = Cross(fActiveRingPlane.Norm(), eyeOnRing - fActiveRingCenter);
154  arrowDir.Normalise();
155  TGLUtil::DrawLine(eyeOnRing, arrowDir*ringRadius*1.3, TGLUtil::kLineHeadArrow, baseScale, TGLUtil::fgYellow);
156  TGLUtil::DrawLine(eyeOnRing, -arrowDir*ringRadius*1.3, TGLUtil::kLineHeadArrow, baseScale, TGLUtil::fgYellow);
157  } else {
158  TGLVector3 activeVector = fRingLine.Vector();
159  activeVector.Normalise();
160  activeVector *= ringRadius;
161  TGLUtil::DrawLine(fRingLine.Start(), activeVector,
162  TGLUtil::kLineHeadNone, baseScale, TGLUtil::fgYellow);
163  }
164  }
165 
166  glEnable(GL_CULL_FACE);
167  glDisable(GL_BLEND);
168 }
169 
170 ////////////////////////////////////////////////////////////////////////////////
171 /// Handle mouse button event over manipulator - returns kTRUE if
172 /// redraw required kFALSE otherwise.
173 
174 Bool_t TGLRotateManip::HandleButton(const Event_t& event, const TGLCamera& camera)
175 {
176  Bool_t captured = TGLManip::HandleButton(event, camera);
177 
178  if (captured) {
179  // Find active selected axis
180  UInt_t axisIndex = fSelectedWidget - 1; // Ugg sort out axis / widget id mapping
181  TGLVector3 widgetAxis = fShape->BoundingBox().Axis(axisIndex, kTRUE);
182 
183  // Construct plane for the axis ring, using normal and center point
184  fActiveRingPlane.Set(widgetAxis, fShape->BoundingBox().Center());
185  fActiveRingCenter.Set(fShape->BoundingBox().Center());
186 
187  fRingLineOld = fRingLine = CalculateRingLine(fLastMouse, camera);
188 
189  // Is plane at shallow angle to eye line if angle between normal of plane and
190  // eye line is ~90 deg (PI/4)
191  Double_t planeEyeAngle = Angle(fActiveRingPlane.Norm(), camera.EyeDirection()) - TMath::ASin(1.0);
192  Double_t shallowDelta = 0.15;
193  if ((planeEyeAngle > -shallowDelta) && (planeEyeAngle < shallowDelta)) {
194  fShallowRing = kTRUE;
195 
196  // Work out ring follow direction - if clicked on back or front of ring.
197  // If plane/eye angle very shallow force to front
198 
199  /* DISABLED - Force onto front always */
200  fShallowFront = kTRUE;
201  /*
202  if ((planeEyeAngle > -shallowDelta/3.0) && (planeEyeAngle < shallowDelta/3.0) ||
203  Dot(fRingLine.Vector(), camera.FrustumPlane(TGLCamera::kNear).Norm()) < 0.0) {
204  fShallowFront = kTRUE;
205  } else {
206  fShallowFront = kFALSE;
207  }*/
208  } else {
209  fShallowRing = kFALSE;
210  }
211  }
212 
213  return captured;
214 }
215 
216 ////////////////////////////////////////////////////////////////////////////////
217 /// Handle mouse motion over manipulator - if active (selected
218 /// widget) rotate physical around selected ring widget plane
219 /// normal. Returns kTRUE if redraw required kFALSE otherwise.
220 
221 Bool_t TGLRotateManip::HandleMotion(const Event_t& event, const TGLCamera& camera)
222 {
223  if (fActive) {
224  TPoint newMouse(event.fX, event.fY);
225 
226  // Calculate singed angle delta between old and new ring position using
227  Double_t angle = CalculateAngleDelta(newMouse, camera);
228  fShape->Rotate(fActiveRingCenter, fActiveRingPlane.Norm(), angle);
229  fLastMouse = newMouse;
230  return kTRUE;
231  }
232  return kFALSE;
233 }
234 
235 ////////////////////////////////////////////////////////////////////////////////
236 /// Calculate angle delta for rotation based on new mouse position.
237 
238 Double_t TGLRotateManip::CalculateAngleDelta(const TPoint& mouse, const TGLCamera& camera)
239 {
240  if (fShallowRing) {
241  std::pair<Bool_t, TGLLine3> nearLineIntersection = Intersection(fActiveRingPlane,
242  camera.FrustumPlane(TGLCamera::kNear));
243  if (!nearLineIntersection.first) {
244  Error("TGLRotateManip::CalculateAngleDelta", "active ring plane parallel to near clip?");
245  return 1.0;
246  }
247  TGLLine3 nearLine = nearLineIntersection.second;
248  TGLVector3 activePlaneNear = camera.WorldDeltaToViewport(nearLine.Start(), nearLine.Vector());
249  activePlaneNear.Normalise();
250  TGLVector3 mouseDelta(mouse.GetX() - fLastMouse.GetX(),
251  -(mouse.GetY() - fLastMouse.GetY()),
252  0.0);
253 
254  Double_t angle = Dot(activePlaneNear, mouseDelta) / 150.0;
255  if (fShallowFront) {
256  return -angle;
257  } else {
258  return angle;
259  }
260  } else {
261  fRingLineOld = fRingLine;
262  fRingLine = CalculateRingLine(fLastMouse, camera);
263  return Angle(fRingLineOld.Vector(), fRingLine.Vector(), fActiveRingPlane.Norm());
264  }
265 }
266 
267 ////////////////////////////////////////////////////////////////////////////////
268 /// Calculated interaction line between 'mouse' viewport point, and
269 /// current selected widget (ring), under supplied 'camera'
270 /// projection.
271 
272 TGLLine3 TGLRotateManip::CalculateRingLine(const TPoint& mouse, const TGLCamera& camera) const
273 {
274  // Find mouse position in viewport coords
275  TPoint mouseViewport(mouse);
276  camera.WindowToViewport(mouseViewport);
277 
278  // Find projection of mouse into world
279  TGLLine3 viewportProjection = camera.ViewportToWorld(mouseViewport);
280 
281  // Find rotation line from ring center to this intersection on plane
282  std::pair<Bool_t, TGLVertex3> ringPlaneInter = Intersection(fActiveRingPlane, viewportProjection, kTRUE);
283 
284  // If intersection fails then ring is parallel to eye line - in this case
285  // force line to run from center back towards viewer (opposite eye line)
286  if (!ringPlaneInter.first) {
287  return TGLLine3(fActiveRingCenter, -camera.EyeDirection());
288  }
289  return TGLLine3(fActiveRingCenter, ringPlaneInter.second);
290 }
291