]> git.lizzy.rs Git - plan9front.git/blob - sys/src/libgeometry/quaternion.c
merge
[plan9front.git] / sys / src / libgeometry / quaternion.c
1 /*
2  * Quaternion arithmetic:
3  *      qadd(q, r)      returns q+r
4  *      qsub(q, r)      returns q-r
5  *      qneg(q)         returns -q
6  *      qmul(q, r)      returns q*r
7  *      qdiv(q, r)      returns q/r, can divide check.
8  *      qinv(q)         returns 1/q, can divide check.
9  *      double qlen(p)  returns modulus of p
10  *      qunit(q)        returns a unit quaternion parallel to q
11  * The following only work on unit quaternions and rotation matrices:
12  *      slerp(q, r, a)  returns q*(r*q^-1)^a
13  *      qmid(q, r)      slerp(q, r, .5) 
14  *      qsqrt(q)        qmid(q, (Quaternion){1,0,0,0})
15  *      qtom(m, q)      converts a unit quaternion q into a rotation matrix m
16  *      mtoq(m)         returns a quaternion equivalent to a rotation matrix m
17  */
18 #include <u.h>
19 #include <libc.h>
20 #include <draw.h>
21 #include <geometry.h>
22 void qtom(Matrix m, Quaternion q){
23 #ifndef new
24         m[0][0]=1-2*(q.j*q.j+q.k*q.k);
25         m[0][1]=2*(q.i*q.j+q.r*q.k);
26         m[0][2]=2*(q.i*q.k-q.r*q.j);
27         m[0][3]=0;
28         m[1][0]=2*(q.i*q.j-q.r*q.k);
29         m[1][1]=1-2*(q.i*q.i+q.k*q.k);
30         m[1][2]=2*(q.j*q.k+q.r*q.i);
31         m[1][3]=0;
32         m[2][0]=2*(q.i*q.k+q.r*q.j);
33         m[2][1]=2*(q.j*q.k-q.r*q.i);
34         m[2][2]=1-2*(q.i*q.i+q.j*q.j);
35         m[2][3]=0;
36         m[3][0]=0;
37         m[3][1]=0;
38         m[3][2]=0;
39         m[3][3]=1;
40 #else
41         /*
42          * Transcribed from Ken Shoemake's new code -- not known to work
43          */
44         double Nq = q.r*q.r+q.i*q.i+q.j*q.j+q.k*q.k;
45         double s = (Nq > 0.0) ? (2.0 / Nq) : 0.0;
46         double xs = q.i*s,              ys = q.j*s,             zs = q.k*s;
47         double wx = q.r*xs,             wy = q.r*ys,            wz = q.r*zs;
48         double xx = q.i*xs,             xy = q.i*ys,            xz = q.i*zs;
49         double yy = q.j*ys,             yz = q.j*zs,            zz = q.k*zs;
50         m[0][0] = 1.0 - (yy + zz); m[1][0] = xy + wz;         m[2][0] = xz - wy;
51         m[0][1] = xy - wz;         m[1][1] = 1.0 - (xx + zz); m[2][1] = yz + wx;
52         m[0][2] = xz + wy;         m[1][2] = yz - wx;         m[2][2] = 1.0 - (xx + yy);
53         m[0][3] = m[1][3] = m[2][3] = m[3][0] = m[3][1] = m[3][2] = 0.0;
54         m[3][3] = 1.0;
55 #endif
56 }
57 Quaternion mtoq(Matrix mat){
58 #ifndef new
59 #define EPS     1.387778780781445675529539585113525e-17 /* 2^-56 */
60         double t;
61         Quaternion q;
62         q.r=0.;
63         q.i=0.;
64         q.j=0.;
65         q.k=1.;
66         if((t=.25*(1+mat[0][0]+mat[1][1]+mat[2][2]))>EPS){
67                 q.r=sqrt(t);
68                 t=4*q.r;
69                 q.i=(mat[1][2]-mat[2][1])/t;
70                 q.j=(mat[2][0]-mat[0][2])/t;
71                 q.k=(mat[0][1]-mat[1][0])/t;
72         }
73         else if((t=-.5*(mat[1][1]+mat[2][2]))>EPS){
74                 q.i=sqrt(t);
75                 t=2*q.i;
76                 q.j=mat[0][1]/t;
77                 q.k=mat[0][2]/t;
78         }
79         else if((t=.5*(1-mat[2][2]))>EPS){
80                 q.j=sqrt(t);
81                 q.k=mat[1][2]/(2*q.j);
82         }
83         return q;
84 #else
85         /*
86          * Transcribed from Ken Shoemake's new code -- not known to work
87          */
88         /* This algorithm avoids near-zero divides by looking for a large
89          * component -- first r, then i, j, or k.  When the trace is greater than zero,
90          * |r| is greater than 1/2, which is as small as a largest component can be.
91          * Otherwise, the largest diagonal entry corresponds to the largest of |i|,
92          * |j|, or |k|, one of which must be larger than |r|, and at least 1/2.
93          */
94         Quaternion qu;
95         double tr, s;
96         
97         tr = mat[0][0] + mat[1][1] + mat[2][2];
98         if (tr >= 0.0) {
99                 s = sqrt(tr + mat[3][3]);
100                 qu.r = s*0.5;
101                 s = 0.5 / s;
102                 qu.i = (mat[2][1] - mat[1][2]) * s;
103                 qu.j = (mat[0][2] - mat[2][0]) * s;
104                 qu.k = (mat[1][0] - mat[0][1]) * s;
105         }
106         else {
107                 int i = 0;
108                 if (mat[1][1] > mat[0][0]) i = 1;
109                 if (mat[2][2] > mat[i][i]) i = 2;
110                 switch(i){
111                 case 0:
112                         s = sqrt( (mat[0][0] - (mat[1][1]+mat[2][2])) + mat[3][3] );
113                         qu.i = s*0.5;
114                         s = 0.5 / s;
115                         qu.j = (mat[0][1] + mat[1][0]) * s;
116                         qu.k = (mat[2][0] + mat[0][2]) * s;
117                         qu.r = (mat[2][1] - mat[1][2]) * s;
118                         break;
119                 case 1:
120                         s = sqrt( (mat[1][1] - (mat[2][2]+mat[0][0])) + mat[3][3] );
121                         qu.j = s*0.5;
122                         s = 0.5 / s;
123                         qu.k = (mat[1][2] + mat[2][1]) * s;
124                         qu.i = (mat[0][1] + mat[1][0]) * s;
125                         qu.r = (mat[0][2] - mat[2][0]) * s;
126                         break;
127                 case 2:
128                         s = sqrt( (mat[2][2] - (mat[0][0]+mat[1][1])) + mat[3][3] );
129                         qu.k = s*0.5;
130                         s = 0.5 / s;
131                         qu.i = (mat[2][0] + mat[0][2]) * s;
132                         qu.j = (mat[1][2] + mat[2][1]) * s;
133                         qu.r = (mat[1][0] - mat[0][1]) * s;
134                         break;
135                 }
136         }
137         if (mat[3][3] != 1.0){
138                 s=1/sqrt(mat[3][3]);
139                 qu.r*=s;
140                 qu.i*=s;
141                 qu.j*=s;
142                 qu.k*=s;
143         }
144         return (qu);
145 #endif
146 }
147 Quaternion qadd(Quaternion q, Quaternion r){
148         q.r+=r.r;
149         q.i+=r.i;
150         q.j+=r.j;
151         q.k+=r.k;
152         return q;
153 }
154 Quaternion qsub(Quaternion q, Quaternion r){
155         q.r-=r.r;
156         q.i-=r.i;
157         q.j-=r.j;
158         q.k-=r.k;
159         return q;
160 }
161 Quaternion qneg(Quaternion q){
162         q.r=-q.r;
163         q.i=-q.i;
164         q.j=-q.j;
165         q.k=-q.k;
166         return q;
167 }
168 Quaternion qmul(Quaternion q, Quaternion r){
169         Quaternion s;
170         s.r=q.r*r.r-q.i*r.i-q.j*r.j-q.k*r.k;
171         s.i=q.r*r.i+r.r*q.i+q.j*r.k-q.k*r.j;
172         s.j=q.r*r.j+r.r*q.j+q.k*r.i-q.i*r.k;
173         s.k=q.r*r.k+r.r*q.k+q.i*r.j-q.j*r.i;
174         return s;
175 }
176 Quaternion qdiv(Quaternion q, Quaternion r){
177         return qmul(q, qinv(r));
178 }
179 Quaternion qunit(Quaternion q){
180         double l=qlen(q);
181         q.r/=l;
182         q.i/=l;
183         q.j/=l;
184         q.k/=l;
185         return q;
186 }
187 /*
188  * Bug?: takes no action on divide check
189  */
190 Quaternion qinv(Quaternion q){
191         double l=q.r*q.r+q.i*q.i+q.j*q.j+q.k*q.k;
192         q.r/=l;
193         q.i=-q.i/l;
194         q.j=-q.j/l;
195         q.k=-q.k/l;
196         return q;
197 }
198 double qlen(Quaternion p){
199         return sqrt(p.r*p.r+p.i*p.i+p.j*p.j+p.k*p.k);
200 }
201 Quaternion slerp(Quaternion q, Quaternion r, double a){
202         double u, v, ang, s;
203         double dot=q.r*r.r+q.i*r.i+q.j*r.j+q.k*r.k;
204         ang=dot<-1?PI:dot>1?0:acos(dot); /* acos gives NaN for dot slightly out of range */
205         s=sin(ang);
206         if(s==0) return ang<PI/2?q:r;
207         u=sin((1-a)*ang)/s;
208         v=sin(a*ang)/s;
209         q.r=u*q.r+v*r.r;
210         q.i=u*q.i+v*r.i;
211         q.j=u*q.j+v*r.j;
212         q.k=u*q.k+v*r.k;
213         return q;
214 }
215 /*
216  * Only works if qlen(q)==qlen(r)==1
217  */
218 Quaternion qmid(Quaternion q, Quaternion r){
219         double l;
220         q=qadd(q, r);
221         l=qlen(q);
222         if(l<1e-12){
223                 q.r=r.i;
224                 q.i=-r.r;
225                 q.j=r.k;
226                 q.k=-r.j;
227         }
228         else{
229                 q.r/=l;
230                 q.i/=l;
231                 q.j/=l;
232                 q.k/=l;
233         }
234         return q;
235 }
236 /*
237  * Only works if qlen(q)==1
238  */
239 static Quaternion qident={1,0,0,0};
240 Quaternion qsqrt(Quaternion q){
241         return qmid(q, qident);
242 }