1 ///
2 /// Copyright (c) 2012, Texas A&M University
3 /// All rights reserved.
4 ///
5 /// Redistribution and use in source and binary forms, with or without
6 /// modification, are permitted provided that the following conditions
7 /// are met:
8 ///
9 ///  * Redistributions of source code must retain the above copyright
10 ///    notice, this list of conditions and the following disclaimer.
11 ///  * Redistributions in binary form must reproduce the above
12 ///    copyright notice, this list of conditions and the following
13 ///    disclaimer in the documentation and/or other materials provided
14 ///    with the distribution.
15 ///  * Neither the name of Texas A&M University nor the names of its
16 ///    contributors may be used to endorse or promote products derived
17 ///    from this software without specific prior written permission.
18 ///
19 /// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 /// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 /// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22 /// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23 /// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24 /// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25 /// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26 /// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 /// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 /// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29 /// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 /// POSSIBILITY OF SUCH DAMAGE.
31 ///
32 /// The following software was written as part of a collaboration with the
33 /// University of South Carolina, Interdisciplinary Mathematics Institute.
34 ///
35 
36 /// @file trackball.cpp
37 /// @details Generic object for generating rotations given mouse input.  This
38 /// class has been based on
39 /// @author Matthew Hielsberg
40 
41 #include <limits>
42 #include <pcl/apps/point_cloud_editor/common.h>
43 #include <pcl/apps/point_cloud_editor/trackball.h>
44 
TrackBall()45 TrackBall::TrackBall() : quat_(1.0f), origin_x_(0), origin_y_(0), origin_z_(0)
46 {
47   radius_sqr_ = (TRACKBALL_RADIUS_SCALE * static_cast<float>(WINDOW_WIDTH)) *
48                 (TRACKBALL_RADIUS_SCALE * static_cast<float>(WINDOW_WIDTH));
49 }
50 
TrackBall(const TrackBall & copy)51 TrackBall::TrackBall(const TrackBall &copy) :
52   quat_(copy.quat_), origin_x_(copy.origin_x_), origin_y_(copy.origin_y_),
53   origin_z_(copy.origin_z_), radius_sqr_(copy.radius_sqr_)
54 {
55 
56 }
57 
~TrackBall()58 TrackBall::~TrackBall()
59 {
60 
61 }
62 
63 TrackBall&
operator =(const TrackBall & rhs)64 TrackBall::operator=(const TrackBall &rhs)
65 {
66   quat_ = rhs.quat_;
67   origin_x_ = rhs.origin_x_;
68   origin_y_ = rhs.origin_y_;
69   origin_z_ = rhs.origin_z_;
70   radius_sqr_ = rhs.radius_sqr_;
71   return *this;
72 }
73 
74 void
start(int s_x,int s_y)75 TrackBall::start(int s_x, int s_y)
76 {
77   getPointFromScreenPoint(s_x, s_y, origin_x_, origin_y_, origin_z_);
78 }
79 
80 void
normalize(float x,float y,float z,float & nx,float & ny,float & nz)81 normalize(float x, float y, float z, float &nx, float &ny, float &nz)
82 {
83     float inv_len = 1.0f / ::sqrt(x * x + y * y + z * z);
84     nx = x * inv_len;
85     ny = y * inv_len;
86     nz = z * inv_len;
87 }
88 
89 boost::math::quaternion<float>
normalizeQuaternion(const boost::math::quaternion<float> & q)90 normalizeQuaternion(const boost::math::quaternion<float> &q)
91 {
92     float w = q.R_component_1();
93     float x = q.R_component_2();
94     float y = q.R_component_3();
95     float z = q.R_component_4();
96     float inv_len = 1.0f / ::sqrt(w * w + x * x + y * y + z * z);
97     return boost::math::quaternion<float>(w * inv_len, x * inv_len, y * inv_len,
98                                           z * inv_len);
99 }
100 
101 boost::math::quaternion<float>
quaternionFromAngleAxis(float angle,float x,float y,float z)102 quaternionFromAngleAxis(float angle, float x, float y, float z)
103 {
104   float s = std::sin(0.5f * angle);
105   float qw = std::cos(0.5f * angle);
106   float qx = x * s;
107   float qy = y * s;
108   float qz = z * s;
109   return normalizeQuaternion(boost::math::quaternion<float>(qw, qx, qy, qz));
110 }
111 
112 boost::math::quaternion<float>
multiplyQuaternion(const boost::math::quaternion<float> & lhs,const boost::math::quaternion<float> & rhs)113 multiplyQuaternion(const boost::math::quaternion<float> &lhs,
114                    const boost::math::quaternion<float> &rhs)
115 {
116     float lw = lhs.R_component_1();
117     float lx = lhs.R_component_2();
118     float ly = lhs.R_component_3();
119     float lz = lhs.R_component_4();
120 
121     float rw = rhs.R_component_1();
122     float rx = rhs.R_component_2();
123     float ry = rhs.R_component_3();
124     float rz = rhs.R_component_4();
125 
126     float tw = lw * rw - lx * rx - ly * ry - lz * rz;
127     float tx = lw * rx + lx * rw - ly * rz + lz * ry;
128     float ty = lw * ry + lx * rz + ly * rw - lz * rx;
129     float tz = lw * rz - lx * ry + ly * rx + lz * rw;
130 
131     return boost::math::quaternion<float>(tw, tx, ty, tz);
132 }
133 
134 void
update(int s_x,int s_y)135 TrackBall::update(int s_x, int s_y)
136 {
137   float cur_x, cur_y, cur_z;
138   getPointFromScreenPoint(s_x, s_y, cur_x, cur_y, cur_z);
139 
140   float d_x = cur_x - origin_x_;
141   float d_y = cur_y - origin_y_;
142   float d_z = cur_z - origin_z_;
143 
144   float dot = d_x * d_x + d_y * d_y + d_z * d_z;
145   if (dot < std::numeric_limits<float>::epsilon())
146   {
147     quat_ = boost::math::quaternion<float>(1.0f);
148     return;
149   }
150   float nc_x, nc_y, nc_z;
151   float no_x, no_y, no_z;
152   normalize(cur_x, cur_y, cur_z, nc_x, nc_y, nc_z);
153   normalize(origin_x_, origin_y_, origin_z_, no_x, no_y, no_z);
154 
155   // compute the angle of rotation
156   float angle = std::acos(nc_x * no_x + nc_y * no_y + nc_z * no_z);
157 
158   // compute the axis of rotation
159   float cross_x = nc_y * no_z - nc_z * no_y;
160   float cross_y = nc_z * no_x - nc_x * no_z;
161   float cross_z = nc_x * no_y - nc_y * no_x;
162 
163   // reuse of nc_*
164   normalize(cross_x, cross_y, cross_z, nc_x, nc_y, nc_z);
165 
166   quat_ = quaternionFromAngleAxis(angle, nc_x, nc_y, nc_z);
167   if (std::isnan(quat_.R_component_1()))
168     quat_ = boost::math::quaternion<float>(1.0f);
169 
170   origin_x_ = cur_x;
171   origin_y_ = cur_y;
172   origin_z_ = cur_z;
173 }
174 
175 void
getRotationMatrix(float (& rot)[MATRIX_SIZE])176 TrackBall::getRotationMatrix(float (&rot)[MATRIX_SIZE])
177 {
178   // This function is based on quaternion_to_R3_rotation from
179   // http://www.boost.org/doc/libs/1_41_0/libs/math/quaternion/HSO3.hpp
180 
181   float a = quat_.R_component_1();
182   float b = quat_.R_component_2();
183   float c = quat_.R_component_3();
184   float d = quat_.R_component_4();
185 
186   float aa = a*a;
187   float ab = a*b;
188   float ac = a*c;
189   float ad = a*d;
190   float bb = b*b;
191   float bc = b*c;
192   float bd = b*d;
193   float cc = c*c;
194   float cd = c*d;
195   float dd = d*d;
196 
197   setIdentity(rot);
198   float n = aa + bb + cc + dd;
199 
200   if (n <= std::numeric_limits<float>::epsilon())
201     return;
202 
203   // fill the upper 3x3
204   rot[0] = (aa + bb - cc - dd);
205   rot[1] = 2 * (-ad + bc);
206   rot[2] = 2 * (ac + bd);
207   rot[MATRIX_SIZE_DIM+0] = 2 * (ad + bc);
208   rot[MATRIX_SIZE_DIM+1] = (aa - bb + cc - dd);
209   rot[MATRIX_SIZE_DIM+2] = 2 * (-ab + cd);
210   rot[2*MATRIX_SIZE_DIM+0] = 2 * (-ac + bd);
211   rot[2*MATRIX_SIZE_DIM+1] = 2 * (ab + cd);
212   rot[2*MATRIX_SIZE_DIM+2] = (aa - bb - cc + dd);
213 }
214 
215 void
reset()216 TrackBall::reset()
217 {
218   quat_ = boost::math::quaternion<float>(1.0f);
219 }
220 
221 void
getPointFromScreenPoint(int s_x,int s_y,float & x,float & y,float & z) const222 TrackBall::getPointFromScreenPoint(int s_x, int s_y,
223                                    float &x, float &y, float &z) const
224 {
225   // See http://www.opengl.org/wiki/Trackball for more info
226 
227   x = static_cast<float>(s_x) - (static_cast<float>(WINDOW_WIDTH) * 0.5f);
228   y = (static_cast<float>(WINDOW_HEIGHT) * 0.5f) - static_cast<float>(s_y);
229   float d = x * x + y * y;
230   if (d > 0.5f * radius_sqr_)
231   {
232     // use hyperbolic sheet
233     z = (0.5f * radius_sqr_) / ::sqrt(d);
234     return;
235   }
236   // use sphere
237   z = ::sqrt(radius_sqr_ - d);
238 }
239 
240