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 ©) :
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