Signal/Geometry Processing Library (SPL)  1.1.24
Arcball.hpp
Go to the documentation of this file.
1 // Copyright (c) 2011 Michael D. Adams
2 // All rights reserved.
3 
4 // __START_OF_LICENSE__
5 //
6 // Copyright (c) 2015 Michael D. Adams
7 // All rights reserved.
8 //
9 // This file is part of the Signal Processing Library (SPL).
10 //
11 // This program is free software; you can redistribute it and/or
12 // modify it under the terms of the GNU General Public License as
13 // published by the Free Software Foundation; either version 3,
14 // or (at your option) any later version.
15 //
16 // This program is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 // GNU General Public License for more details.
20 //
21 // You should have received a copy of the GNU General Public
22 // License along with this program; see the file LICENSE. If not,
23 // see <http://www.gnu.org/licenses/>.
24 //
25 // __END_OF_LICENSE__
26 
32 #ifndef SPL_Arcball_hpp
33 #define SPL_Arcball_hpp
34 
36 // Header files
38 
39 #include <SPL/config.hpp>
40 #include <CGAL/Plane_3.h>
41 #include <CGAL/Ray_3.h>
42 #include "cgalUtil.hpp"
43 
45 //
47 
48 namespace SPL {
49 
55 // Geometry utility functions
58 
63 template <class T>
64 typename T::Point_3 closestPointOnRay(const typename CGAL::Point_3<T>&
65  rayOrigin, const typename CGAL::Vector_3<T>& rayDir,
66  const typename CGAL::Point_3<T>& point)
67 {
68  typename T::FT t = (rayDir * (point - rayOrigin)) / (rayDir * rayDir);
69  if (t < 0) {
70  t = 0;
71  }
72  return typename T::Point_3(rayOrigin + t * rayDir);
73 }
74 
89 template <class T>
90 std::pair<bool, typename T::Point_3> findRaySphereIntersection(
91  const typename CGAL::Point_3<T>& sphereCenter, typename T::FT sphereRadius,
92  const typename CGAL::Point_3<T>& rayOrigin,
93  const typename CGAL::Vector_3<T>& rayDir)
94 {
95  typename T::FT a = rayDir * rayDir;
96  typename T::Vector_3 delta = rayOrigin - sphereCenter;
97  typename T::FT b = 2.0 * delta * rayDir;
98  typename T::FT c = delta * delta - sphereRadius * sphereRadius;
99  typename T::FT discrim = b * b - 4.0 * a * c;
100  if (discrim < 0.0) {
101  return std::pair<bool, typename T::Point_3>(false,
102  closestPointOnRay(rayOrigin, rayDir, sphereCenter));
103  }
104  typename T::FT d = sqrt(discrim);
105  typename T::FT t0 = (-b - d) / (2.0 * a);
106  typename T::FT t1 = (-b + d) / (2.0 * a);
107  typename T::FT t = t0;
108  if (t1 >= 0 && t1 < t0) {
109  t = t1;
110  }
111  if (t < 0.0) {
112  return std::pair<bool, typename T::Point_3>(false,
113  closestPointOnRay(rayOrigin, rayDir, sphereCenter));
114  }
115  return std::pair<bool, typename T::Point_3>(true, rayOrigin + t * rayDir);
116 }
117 
131 template <class T>
132 std::pair<bool, typename T::Point_3> findRayPlaneIntersection(
133  const typename CGAL::Point_3<T>& planePoint,
134  const typename CGAL::Vector_3<T>& planeNormal,
135  const typename CGAL::Point_3<T>& rayOrigin,
136  const typename CGAL::Vector_3<T>& rayDir)
137 {
138  typename T::Point_3 intersectPoint;
139  typename T::Ray_3 intersectRay;
140  CGAL::Object result = CGAL::intersection(typename T::Plane_3(planePoint,
141  planeNormal), typename T::Ray_3(rayOrigin, rayDir));
142  if (CGAL::assign(intersectPoint, result)) {
143  return std::pair<bool, typename T::Point_3>(true, intersectPoint);
144  } else if (CGAL::assign(intersectRay, result)) {
145  return std::pair<bool, typename T::Point_3>(true,
146  intersectRay.source());
147  } else {
148  return std::pair<bool, typename T::Point_3>(false, rayOrigin);
149  }
150 }
151 
159 template <class T>
160 class Arcball
161 {
162 public:
163 
165  typedef T Kernel;
166 
168  typedef typename Kernel::Point_3 Point;
169 
171  typedef typename Kernel::Vector_3 Vector;
172 
175 
179  Arcball();
180 
184  void initialize(double arcBallRadius, const Point& eyePos,
185  const Vector& eyeDir, const Vector& eyeUp, const Point& sceneCenter);
186 
190  void setMode(int mode);
191 
195  void start(const Point& pos);
196 
200  void move(const Point& pos);
201 
205  void clear();
206 
211  Rotation getRotation() const;
212 
216  static Rotation combineRotations(const Rotation&, const Rotation&);
217 
221  void setDebugLevel(int debugLevel) const;
222 
223 private:
224 
225  // The arcball rotation mode.
226  int mode_;
227 
228  // The arcball radius.
229  double arcBallRadius_;
230 
231  // The eye position.
232  Point eyePos_;
233 
234  // The eye up direction.
235  Vector eyeUp_;
236 
237  // Indicates if the start position has been initialized.
238  bool startPosValid_;
239 
240  // The start position for arcball movement.
241  Point startPos_;
242 
243  // The current position for arcball movement.
244  Point curPos_;
245 
246  // The scene center.
247  Point sceneCenter_;
248 
249  // The level of debugging output.
250  int debugLevel_;
251 
252 };
253 
254 template <class T>
255 Arcball<T>::Arcball() : mode_(0), arcBallRadius_(1), eyePos_(0, 0, 0),
256  eyeUp_(0, 0, 0), startPosValid_(false), startPos_(0, 0, 0),
257  curPos_(0, 0, 0), sceneCenter_(0, 0, 0), debugLevel_(0)
258 {
259 }
260 
261 template <class T>
262 void Arcball<T>::initialize(double arcBallRadius,
263  const typename T::Point_3& eyePos,
264  const typename T::Vector_3& eyeDir,
265  const typename T::Vector_3& eyeUp,
266  const typename T::Point_3& sceneCenter)
267 {
268  mode_ = 0;
269  arcBallRadius_ = arcBallRadius;
270  eyePos_ = eyePos;
271  sceneCenter_ = sceneCenter;
272  eyeUp_ = eyeUp;
273  clear();
274 }
275 
276 template <class T>
277 void Arcball<T>::setMode(int mode)
278 {
279  mode_ = mode;
280 }
281 
282 template <class T>
284 {
285  startPos_ = pos;
286  curPos_ = pos;
287  startPosValid_ = true;
288  if (debugLevel_ > 0) {
289  std::cerr
290  << "start: startPos=" << startPos_ << " curPos=" << curPos_ << "\n";
291  }
292 }
293 
294 template <class T>
296 {
297  curPos_ = pos;
298  if (debugLevel_ > 0) {
299  std::cerr
300  << "move: startPos=" << startPos_ << " curPos=" << curPos_ << "\n";
301  }
302 }
303 
304 template <class T>
306 {
307  startPosValid_ = false;
308  startPos_ = typename T::Point_3(0, 0, 0);
309  curPos_ = startPos_;
310  if (debugLevel_ > 0) {
311  std::cerr
312  << "clear: startPos=" << startPos_ << " curPos=" << curPos_ << "\n";
313  }
314 }
315 
316 template <class T>
318 {
319  // Ensure that the starting position has been initialized by the user.
320  assert(startPosValid_);
321 
322  if (debugLevel_ > 0) {
323  std::cerr << "startPos=" << startPos_ << " curPos=" << curPos_ << "\n";
324  }
325 
326  // Calculate the required rotation based on the mouse movement.
327  std::pair<bool, typename T::Point_3> oldResult;
328  std::pair<bool, typename T::Point_3> curResult;
329  if (mode_ == 0) {
330  oldResult = findRaySphereIntersection<T>(sceneCenter_,
331  arcBallRadius_, eyePos_, startPos_ - eyePos_);
332  curResult = findRaySphereIntersection<T>(sceneCenter_,
333  arcBallRadius_, eyePos_, curPos_ - eyePos_);
334  if (!oldResult.first && !curResult.first) {
335  oldResult = findRayPlaneIntersection<T>(sceneCenter_,
336  eyePos_ - sceneCenter_, eyePos_, startPos_ - eyePos_);
337  curResult = findRayPlaneIntersection<T>(sceneCenter_,
338  eyePos_ - sceneCenter_, eyePos_, curPos_ - eyePos_);
339  oldResult.first = true;
340  curResult.first = true;
341  }
342  } else {
343  typename T::Vector_3 planeNormal(eyePos_ - sceneCenter_);
344  oldResult = findRayPlaneIntersection<T>(sceneCenter_, planeNormal,
345  eyePos_, startPos_ - eyePos_);
346  curResult = findRayPlaneIntersection<T>(sceneCenter_, planeNormal,
347  eyePos_, curPos_ - eyePos_);
348  }
349 
350  // If the above calculation succeeded, apply the rotation.
351  Rotation result(typename T::Vector_3(0, 0, 1), 0);
352  if (oldResult.first && curResult.first) {
353  typename T::Vector_3 curVec = normalize(curResult.second -
354  sceneCenter_);
355  typename T::Vector_3 oldVec = normalize(oldResult.second -
356  sceneCenter_);
357  if (norm(curVec - oldVec) > 1e-20) {
358  Quaternion<T> q = Quaternion<T>(0, curVec) / Quaternion<T>(0,
359  oldVec);
360  result = quaternionToRotation(q);
361  }
362  }
363 
364  if (debugLevel_ > 0) {
365  std::cerr << "result=" << result.axis << " " << result.angle << "\n";
366  }
367  return result;
368 }
369 
370 template <class T>
372  const Arcball<T>::Rotation& r0, const Arcball<T>::Rotation& r1)
373 {
376 }
377 
380 
385 }
386 
387 #endif
T::Vector_3 normalize(const typename CGAL::Vector_3< T > &v)
Compute a unit vector.
Definition: cgalUtil.hpp:79
A quaternion represented in terms of its scalar and vector parts.
Definition: cgalUtil.hpp:133
static Rotation combineRotations(const Rotation &, const Rotation &)
Combine two rotations.
Definition: Arcball.hpp:371
void clear()
Clear the starting and current positions for the arcball.
Definition: Arcball.hpp:305
void setDebugLevel(int debugLevel) const
For debugging...
Kernel::Vector_3 Vector
The vector type.
Definition: Arcball.hpp:171
Definition: Arcball.hpp:48
void initialize(double arcBallRadius, const Point &eyePos, const Vector &eyeDir, const Vector &eyeUp, const Point &sceneCenter)
Initialize the state of an arcball.
Definition: Arcball.hpp:262
Rotation_3< T > quaternionToRotation(const Quaternion< T > &q)
Convert a unit-norm quaternion into its corresponding rotation.
Definition: cgalUtil.hpp:198
Kernel::Point_3 Point
The point type.
Definition: Arcball.hpp:168
T::FT norm(const typename CGAL::Vector_3< T > &v)
Compute the norm of a vector.
Definition: cgalUtil.hpp:66
Arcball.
Definition: Arcball.hpp:160
Real angle
The angle of rotation.
Definition: cgalUtil.hpp:124
A 3-D rotation.
Definition: cgalUtil.hpp:106
Quaternion< T > rotationToQuaternion(const Rotation_3< T > &rot)
Convert a rotation into its corresponding quaternion.
Definition: cgalUtil.hpp:187
void start(const Point &pos)
Set the starting position for arcball movement.
Definition: Arcball.hpp:283
std::pair< bool, typename T::Point_3 > findRayPlaneIntersection(const typename CGAL::Point_3< T > &planePoint, const typename CGAL::Vector_3< T > &planeNormal, const typename CGAL::Point_3< T > &rayOrigin, const typename CGAL::Vector_3< T > &rayDir)
Compute the intersection of a ray and a plane.
Definition: Arcball.hpp:132
T Kernel
The CGAL kernel.
Definition: Arcball.hpp:165
void move(const Point &pos)
Set the current position for arcball movement.
Definition: Arcball.hpp:295
Rotation_3< Kernel > Rotation
The representation of a rotation.
Definition: Arcball.hpp:174
std::pair< bool, typename T::Point_3 > findRaySphereIntersection(const typename CGAL::Point_3< T > &sphereCenter, typename T::FT sphereRadius, const typename CGAL::Point_3< T > &rayOrigin, const typename CGAL::Vector_3< T > &rayDir)
Compute the intersection of a ray and a sphere.
Definition: Arcball.hpp:90
Arcball()
Create an arcball.
Definition: Arcball.hpp:255
Rotation getRotation() const
Get the rotation required to turn the arcball from the starting position to the current position...
Definition: Arcball.hpp:317
void setMode(int mode)
Set the arcball rotation mode.
Definition: Arcball.hpp:277
Vector_3 axis
The axis of rotation.
Definition: cgalUtil.hpp:121
T::Point_3 closestPointOnRay(const typename CGAL::Point_3< T > &rayOrigin, const typename CGAL::Vector_3< T > &rayDir, const typename CGAL::Point_3< T > &point)
Compute the closest point on a ray to the specified point.
Definition: Arcball.hpp:64
This file contains various CGAL utility code.