1 /************************************************************************
2
3 Arcball rotation control. See the original article
4
5 "Arcball Rotation Control"
6 by Ken Shoemake <shoemake@graphics.cis.upenn.edu>
7 in "Graphics Gems IV", Academic Press, 1994.
8
9 for more details.
10
11 $Id: arcball.cxx 427 2004-09-27 04:45:31Z garland $
12
13 ************************************************************************/
14
15 #include <gfx/arcball.h>
16 #include <gfx/gl.h>
17 #include <sstream>
18
19 namespace gfx
20 {
21
22 // Converts to points on unit sphere into a unit quaternion
quat_from_sphere(const Vec3 & from,const Vec3 & to)23 static Quat quat_from_sphere(const Vec3& from, const Vec3& to)
24 {
25 Vec3 v;
26 v[0] = from[1]*to[2] - from[2]*to[1];
27 v[1] = from[2]*to[0] - from[0]*to[2];
28 v[2] = from[0]*to[1] - from[1]*to[0];
29
30 double s = from*to;
31
32 return Quat(v, s);
33 }
34
35
proj_to_sphere(const Vec2 & mouse)36 Vec3 Arcball::proj_to_sphere(const Vec2& mouse)
37 {
38 Vec2 p = (mouse - ball_ctr) / ball_radius;
39 double mag = p*p;
40
41 if( mag > 1.0 )
42 {
43 double s = sqrt(mag);
44 return Vec3(p[0]/s, p[1]/s, 0.0);
45 }
46 else
47 {
48 return Vec3(p[0], p[1], sqrt(1-mag));
49 }
50 }
51
update()52 void Arcball::update()
53 {
54 // constrain v_from & v_to to axes here, if necessary
55
56 if( is_dragging )
57 {
58 q_drag = quat_from_sphere(v_from, v_to);
59 q_now = q_drag * q_down;
60 }
61 }
62
Arcball()63 Arcball::Arcball()
64 {
65 ball_ctr = Vec2(0, 0);
66 ball_radius = 1.0;
67
68 q_now = Quat::ident();
69 q_down = Quat::ident();
70 q_drag = Quat::ident();
71
72 is_dragging = false;
73 }
74
mouse_down(int * where,int which)75 bool Arcball::mouse_down(int *where, int which)
76 {
77 float vp[4];
78 glGetFloatv(GL_VIEWPORT, vp);
79 float W=vp[2], H=vp[3];
80
81 if( which==1 )
82 {
83 is_dragging = true;
84 Vec2 v( (2.0 * where[0] - W)/W, (H - 2.0 * where[1])/H );
85 v_from = proj_to_sphere(v);
86 v_to = v_from;
87 }
88
89 return true;
90 }
91
mouse_up(int * where,int which)92 bool Arcball::mouse_up(int *where, int which)
93 {
94 is_dragging = false;
95 q_down = q_now;
96 q_drag = Quat::ident();
97
98 return false;
99 }
100
mouse_drag(int * where,int * last,int which)101 bool Arcball::mouse_drag(int *where, int *last, int which)
102 {
103 float vp[4];
104 glGetFloatv(GL_VIEWPORT, vp);
105 float W=vp[2], H=vp[3];
106
107 float diam = 2*radius;
108
109 if( which==1 )
110 {
111 Vec2 v( (2.0 * where[0] - W)/W, (H - 2.0 * where[1])/H );
112 v_to = proj_to_sphere(v);
113 }
114 else if( which==2 )
115 {
116 trans[0] += diam * (where[0] - last[0]) / W;
117 trans[1] += diam * (last[1] - where[1]) / H;
118 }
119 else if( which==3 )
120 {
121 trans[2] += 0.02*diam*(where[1] - last[1]);
122 }
123 else
124 return false;
125
126 return true;
127 }
128
apply_transform()129 void Arcball::apply_transform()
130 {
131 update();
132 curquat = conjugate(q_now);
133 Baseball::apply_transform();
134 }
135
136
update_animation()137 void Arcball::update_animation()
138 {
139 }
140
get_transform(Vec3 & c,Vec3 & t,Quat & q)141 void Arcball::get_transform(Vec3 & c, Vec3 &t, Quat & q)
142 {
143 c = ctr;
144 t = trans;
145 q = q_now;
146 }
147
set_transform(const Vec3 & c,const Vec3 & t,const Quat & q)148 void Arcball::set_transform(const Vec3 & c, const Vec3 &t, const Quat & q)
149 {
150 ctr = c;
151 trans = t;
152 q_now = q;
153 q_down = q;
154 q_drag = q;
155 }
156
write(std::ostream & out)157 void Arcball::write(std::ostream& out)
158 {
159 out << "arcball ";
160 out << ball_ctr << " " << ball_radius << " ";
161 out << q_now << " " << q_down << " " << q_drag << std::endl;
162 Baseball::write(out);
163 }
164
read(std::istream & in)165 void Arcball::read(std::istream& in)
166 {
167 std::string name;
168 in >> name;
169 in >> ball_ctr >> ball_radius;
170 in >> q_now >> q_down >> q_drag;
171 Baseball::read(in);
172 }
173
174 } // namespace gfx
175