1219b2ee8SDavid du Colombier /*
2219b2ee8SDavid du Colombier * Quaternion arithmetic:
3219b2ee8SDavid du Colombier * qadd(q, r) returns q+r
4219b2ee8SDavid du Colombier * qsub(q, r) returns q-r
5219b2ee8SDavid du Colombier * qneg(q) returns -q
6219b2ee8SDavid du Colombier * qmul(q, r) returns q*r
7219b2ee8SDavid du Colombier * qdiv(q, r) returns q/r, can divide check.
8219b2ee8SDavid du Colombier * qinv(q) returns 1/q, can divide check.
9219b2ee8SDavid du Colombier * double qlen(p) returns modulus of p
10219b2ee8SDavid du Colombier * qunit(q) returns a unit quaternion parallel to q
11219b2ee8SDavid du Colombier * The following only work on unit quaternions and rotation matrices:
12219b2ee8SDavid du Colombier * slerp(q, r, a) returns q*(r*q^-1)^a
13219b2ee8SDavid du Colombier * qmid(q, r) slerp(q, r, .5)
14219b2ee8SDavid du Colombier * qsqrt(q) qmid(q, (Quaternion){1,0,0,0})
15219b2ee8SDavid du Colombier * qtom(m, q) converts a unit quaternion q into a rotation matrix m
16219b2ee8SDavid du Colombier * mtoq(m) returns a quaternion equivalent to a rotation matrix m
17219b2ee8SDavid du Colombier */
18219b2ee8SDavid du Colombier #include <u.h>
19219b2ee8SDavid du Colombier #include <libc.h>
20*7dd7cddfSDavid du Colombier #include <draw.h>
21219b2ee8SDavid du Colombier #include <geometry.h>
qtom(Matrix m,Quaternion q)22219b2ee8SDavid du Colombier void qtom(Matrix m, Quaternion q){
23219b2ee8SDavid du Colombier #ifndef new
24219b2ee8SDavid du Colombier m[0][0]=1-2*(q.j*q.j+q.k*q.k);
25219b2ee8SDavid du Colombier m[0][1]=2*(q.i*q.j+q.r*q.k);
26219b2ee8SDavid du Colombier m[0][2]=2*(q.i*q.k-q.r*q.j);
27219b2ee8SDavid du Colombier m[0][3]=0;
28219b2ee8SDavid du Colombier m[1][0]=2*(q.i*q.j-q.r*q.k);
29219b2ee8SDavid du Colombier m[1][1]=1-2*(q.i*q.i+q.k*q.k);
30219b2ee8SDavid du Colombier m[1][2]=2*(q.j*q.k+q.r*q.i);
31219b2ee8SDavid du Colombier m[1][3]=0;
32219b2ee8SDavid du Colombier m[2][0]=2*(q.i*q.k+q.r*q.j);
33219b2ee8SDavid du Colombier m[2][1]=2*(q.j*q.k-q.r*q.i);
34219b2ee8SDavid du Colombier m[2][2]=1-2*(q.i*q.i+q.j*q.j);
35219b2ee8SDavid du Colombier m[2][3]=0;
36219b2ee8SDavid du Colombier m[3][0]=0;
37219b2ee8SDavid du Colombier m[3][1]=0;
38219b2ee8SDavid du Colombier m[3][2]=0;
39219b2ee8SDavid du Colombier m[3][3]=1;
40219b2ee8SDavid du Colombier #else
41219b2ee8SDavid du Colombier /*
42219b2ee8SDavid du Colombier * Transcribed from Ken Shoemake's new code -- not known to work
43219b2ee8SDavid du Colombier */
44219b2ee8SDavid du Colombier double Nq = q.r*q.r+q.i*q.i+q.j*q.j+q.k*q.k;
45219b2ee8SDavid du Colombier double s = (Nq > 0.0) ? (2.0 / Nq) : 0.0;
46219b2ee8SDavid du Colombier double xs = q.i*s, ys = q.j*s, zs = q.k*s;
47219b2ee8SDavid du Colombier double wx = q.r*xs, wy = q.r*ys, wz = q.r*zs;
48219b2ee8SDavid du Colombier double xx = q.i*xs, xy = q.i*ys, xz = q.i*zs;
49219b2ee8SDavid du Colombier double yy = q.j*ys, yz = q.j*zs, zz = q.k*zs;
50219b2ee8SDavid du Colombier m[0][0] = 1.0 - (yy + zz); m[1][0] = xy + wz; m[2][0] = xz - wy;
51219b2ee8SDavid du Colombier m[0][1] = xy - wz; m[1][1] = 1.0 - (xx + zz); m[2][1] = yz + wx;
52219b2ee8SDavid du Colombier m[0][2] = xz + wy; m[1][2] = yz - wx; m[2][2] = 1.0 - (xx + yy);
53219b2ee8SDavid du Colombier m[0][3] = m[1][3] = m[2][3] = m[3][0] = m[3][1] = m[3][2] = 0.0;
54219b2ee8SDavid du Colombier m[3][3] = 1.0;
55219b2ee8SDavid du Colombier #endif
56219b2ee8SDavid du Colombier }
mtoq(Matrix mat)57219b2ee8SDavid du Colombier Quaternion mtoq(Matrix mat){
58219b2ee8SDavid du Colombier #ifndef new
59219b2ee8SDavid du Colombier #define EPS 1.387778780781445675529539585113525e-17 /* 2^-56 */
60219b2ee8SDavid du Colombier double t;
61*7dd7cddfSDavid du Colombier Quaternion q;
62*7dd7cddfSDavid du Colombier q.r=0.;
63*7dd7cddfSDavid du Colombier q.i=0.;
64*7dd7cddfSDavid du Colombier q.j=0.;
65*7dd7cddfSDavid du Colombier q.k=1.;
66219b2ee8SDavid du Colombier if((t=.25*(1+mat[0][0]+mat[1][1]+mat[2][2]))>EPS){
67219b2ee8SDavid du Colombier q.r=sqrt(t);
68219b2ee8SDavid du Colombier t=4*q.r;
69219b2ee8SDavid du Colombier q.i=(mat[1][2]-mat[2][1])/t;
70219b2ee8SDavid du Colombier q.j=(mat[2][0]-mat[0][2])/t;
71219b2ee8SDavid du Colombier q.k=(mat[0][1]-mat[1][0])/t;
72219b2ee8SDavid du Colombier }
73219b2ee8SDavid du Colombier else if((t=-.5*(mat[1][1]+mat[2][2]))>EPS){
74219b2ee8SDavid du Colombier q.i=sqrt(t);
75219b2ee8SDavid du Colombier t=2*q.i;
76219b2ee8SDavid du Colombier q.j=mat[0][1]/t;
77219b2ee8SDavid du Colombier q.k=mat[0][2]/t;
78219b2ee8SDavid du Colombier }
79219b2ee8SDavid du Colombier else if((t=.5*(1-mat[2][2]))>EPS){
80219b2ee8SDavid du Colombier q.j=sqrt(t);
81219b2ee8SDavid du Colombier q.k=mat[1][2]/(2*q.j);
82219b2ee8SDavid du Colombier }
83219b2ee8SDavid du Colombier return q;
84219b2ee8SDavid du Colombier #else
85219b2ee8SDavid du Colombier /*
86219b2ee8SDavid du Colombier * Transcribed from Ken Shoemake's new code -- not known to work
87219b2ee8SDavid du Colombier */
88219b2ee8SDavid du Colombier /* This algorithm avoids near-zero divides by looking for a large
89219b2ee8SDavid du Colombier * component -- first r, then i, j, or k. When the trace is greater than zero,
90219b2ee8SDavid du Colombier * |r| is greater than 1/2, which is as small as a largest component can be.
91219b2ee8SDavid du Colombier * Otherwise, the largest diagonal entry corresponds to the largest of |i|,
92219b2ee8SDavid du Colombier * |j|, or |k|, one of which must be larger than |r|, and at least 1/2.
93219b2ee8SDavid du Colombier */
94219b2ee8SDavid du Colombier Quaternion qu;
95219b2ee8SDavid du Colombier double tr, s;
96219b2ee8SDavid du Colombier
97219b2ee8SDavid du Colombier tr = mat[0][0] + mat[1][1] + mat[2][2];
98219b2ee8SDavid du Colombier if (tr >= 0.0) {
99219b2ee8SDavid du Colombier s = sqrt(tr + mat[3][3]);
100219b2ee8SDavid du Colombier qu.r = s*0.5;
101219b2ee8SDavid du Colombier s = 0.5 / s;
102219b2ee8SDavid du Colombier qu.i = (mat[2][1] - mat[1][2]) * s;
103219b2ee8SDavid du Colombier qu.j = (mat[0][2] - mat[2][0]) * s;
104219b2ee8SDavid du Colombier qu.k = (mat[1][0] - mat[0][1]) * s;
105219b2ee8SDavid du Colombier }
106219b2ee8SDavid du Colombier else {
107219b2ee8SDavid du Colombier int i = 0;
108219b2ee8SDavid du Colombier if (mat[1][1] > mat[0][0]) i = 1;
109219b2ee8SDavid du Colombier if (mat[2][2] > mat[i][i]) i = 2;
110219b2ee8SDavid du Colombier switch(i){
111219b2ee8SDavid du Colombier case 0:
112219b2ee8SDavid du Colombier s = sqrt( (mat[0][0] - (mat[1][1]+mat[2][2])) + mat[3][3] );
113219b2ee8SDavid du Colombier qu.i = s*0.5;
114219b2ee8SDavid du Colombier s = 0.5 / s;
115219b2ee8SDavid du Colombier qu.j = (mat[0][1] + mat[1][0]) * s;
116219b2ee8SDavid du Colombier qu.k = (mat[2][0] + mat[0][2]) * s;
117219b2ee8SDavid du Colombier qu.r = (mat[2][1] - mat[1][2]) * s;
118219b2ee8SDavid du Colombier break;
119219b2ee8SDavid du Colombier case 1:
120219b2ee8SDavid du Colombier s = sqrt( (mat[1][1] - (mat[2][2]+mat[0][0])) + mat[3][3] );
121219b2ee8SDavid du Colombier qu.j = s*0.5;
122219b2ee8SDavid du Colombier s = 0.5 / s;
123219b2ee8SDavid du Colombier qu.k = (mat[1][2] + mat[2][1]) * s;
124219b2ee8SDavid du Colombier qu.i = (mat[0][1] + mat[1][0]) * s;
125219b2ee8SDavid du Colombier qu.r = (mat[0][2] - mat[2][0]) * s;
126219b2ee8SDavid du Colombier break;
127219b2ee8SDavid du Colombier case 2:
128219b2ee8SDavid du Colombier s = sqrt( (mat[2][2] - (mat[0][0]+mat[1][1])) + mat[3][3] );
129219b2ee8SDavid du Colombier qu.k = s*0.5;
130219b2ee8SDavid du Colombier s = 0.5 / s;
131219b2ee8SDavid du Colombier qu.i = (mat[2][0] + mat[0][2]) * s;
132219b2ee8SDavid du Colombier qu.j = (mat[1][2] + mat[2][1]) * s;
133219b2ee8SDavid du Colombier qu.r = (mat[1][0] - mat[0][1]) * s;
134219b2ee8SDavid du Colombier break;
135219b2ee8SDavid du Colombier }
136219b2ee8SDavid du Colombier }
137219b2ee8SDavid du Colombier if (mat[3][3] != 1.0){
138219b2ee8SDavid du Colombier s=1/sqrt(mat[3][3]);
139219b2ee8SDavid du Colombier qu.r*=s;
140219b2ee8SDavid du Colombier qu.i*=s;
141219b2ee8SDavid du Colombier qu.j*=s;
142219b2ee8SDavid du Colombier qu.k*=s;
143219b2ee8SDavid du Colombier }
144219b2ee8SDavid du Colombier return (qu);
145219b2ee8SDavid du Colombier #endif
146219b2ee8SDavid du Colombier }
qadd(Quaternion q,Quaternion r)147219b2ee8SDavid du Colombier Quaternion qadd(Quaternion q, Quaternion r){
148*7dd7cddfSDavid du Colombier q.r+=r.r;
149*7dd7cddfSDavid du Colombier q.i+=r.i;
150*7dd7cddfSDavid du Colombier q.j+=r.j;
151*7dd7cddfSDavid du Colombier q.k+=r.k;
152*7dd7cddfSDavid du Colombier return q;
153219b2ee8SDavid du Colombier }
qsub(Quaternion q,Quaternion r)154219b2ee8SDavid du Colombier Quaternion qsub(Quaternion q, Quaternion r){
155*7dd7cddfSDavid du Colombier q.r-=r.r;
156*7dd7cddfSDavid du Colombier q.i-=r.i;
157*7dd7cddfSDavid du Colombier q.j-=r.j;
158*7dd7cddfSDavid du Colombier q.k-=r.k;
159*7dd7cddfSDavid du Colombier return q;
160219b2ee8SDavid du Colombier }
qneg(Quaternion q)161219b2ee8SDavid du Colombier Quaternion qneg(Quaternion q){
162*7dd7cddfSDavid du Colombier q.r=-q.r;
163*7dd7cddfSDavid du Colombier q.i=-q.i;
164*7dd7cddfSDavid du Colombier q.j=-q.j;
165*7dd7cddfSDavid du Colombier q.k=-q.k;
166*7dd7cddfSDavid du Colombier return q;
167219b2ee8SDavid du Colombier }
qmul(Quaternion q,Quaternion r)168219b2ee8SDavid du Colombier Quaternion qmul(Quaternion q, Quaternion r){
169*7dd7cddfSDavid du Colombier Quaternion s;
170*7dd7cddfSDavid du Colombier s.r=q.r*r.r-q.i*r.i-q.j*r.j-q.k*r.k;
171*7dd7cddfSDavid du Colombier s.i=q.r*r.i+r.r*q.i+q.j*r.k-q.k*r.j;
172*7dd7cddfSDavid du Colombier s.j=q.r*r.j+r.r*q.j+q.k*r.i-q.i*r.k;
173*7dd7cddfSDavid du Colombier s.k=q.r*r.k+r.r*q.k+q.i*r.j-q.j*r.i;
174*7dd7cddfSDavid du Colombier return s;
175219b2ee8SDavid du Colombier }
qdiv(Quaternion q,Quaternion r)176219b2ee8SDavid du Colombier Quaternion qdiv(Quaternion q, Quaternion r){
177219b2ee8SDavid du Colombier return qmul(q, qinv(r));
178219b2ee8SDavid du Colombier }
qunit(Quaternion q)179219b2ee8SDavid du Colombier Quaternion qunit(Quaternion q){
180219b2ee8SDavid du Colombier double l=qlen(q);
181*7dd7cddfSDavid du Colombier q.r/=l;
182*7dd7cddfSDavid du Colombier q.i/=l;
183*7dd7cddfSDavid du Colombier q.j/=l;
184*7dd7cddfSDavid du Colombier q.k/=l;
185*7dd7cddfSDavid du Colombier return q;
186219b2ee8SDavid du Colombier }
187219b2ee8SDavid du Colombier /*
188219b2ee8SDavid du Colombier * Bug?: takes no action on divide check
189219b2ee8SDavid du Colombier */
qinv(Quaternion q)190219b2ee8SDavid du Colombier Quaternion qinv(Quaternion q){
191219b2ee8SDavid du Colombier double l=q.r*q.r+q.i*q.i+q.j*q.j+q.k*q.k;
192*7dd7cddfSDavid du Colombier q.r/=l;
193*7dd7cddfSDavid du Colombier q.i=-q.i/l;
194*7dd7cddfSDavid du Colombier q.j=-q.j/l;
195*7dd7cddfSDavid du Colombier q.k=-q.k/l;
196*7dd7cddfSDavid du Colombier return q;
197219b2ee8SDavid du Colombier }
qlen(Quaternion p)198219b2ee8SDavid du Colombier double qlen(Quaternion p){
199219b2ee8SDavid du Colombier return sqrt(p.r*p.r+p.i*p.i+p.j*p.j+p.k*p.k);
200219b2ee8SDavid du Colombier }
slerp(Quaternion q,Quaternion r,double a)201219b2ee8SDavid du Colombier Quaternion slerp(Quaternion q, Quaternion r, double a){
202219b2ee8SDavid du Colombier double u, v, ang, s;
203219b2ee8SDavid du Colombier double dot=q.r*r.r+q.i*r.i+q.j*r.j+q.k*r.k;
204219b2ee8SDavid du Colombier ang=dot<-1?PI:dot>1?0:acos(dot); /* acos gives NaN for dot slightly out of range */
205219b2ee8SDavid du Colombier s=sin(ang);
206219b2ee8SDavid du Colombier if(s==0) return ang<PI/2?q:r;
207219b2ee8SDavid du Colombier u=sin((1-a)*ang)/s;
208219b2ee8SDavid du Colombier v=sin(a*ang)/s;
209*7dd7cddfSDavid du Colombier q.r=u*q.r+v*r.r;
210*7dd7cddfSDavid du Colombier q.i=u*q.i+v*r.i;
211*7dd7cddfSDavid du Colombier q.j=u*q.j+v*r.j;
212*7dd7cddfSDavid du Colombier q.k=u*q.k+v*r.k;
213*7dd7cddfSDavid du Colombier return q;
214219b2ee8SDavid du Colombier }
215219b2ee8SDavid du Colombier /*
216219b2ee8SDavid du Colombier * Only works if qlen(q)==qlen(r)==1
217219b2ee8SDavid du Colombier */
qmid(Quaternion q,Quaternion r)218219b2ee8SDavid du Colombier Quaternion qmid(Quaternion q, Quaternion r){
219219b2ee8SDavid du Colombier double l;
220219b2ee8SDavid du Colombier q=qadd(q, r);
221219b2ee8SDavid du Colombier l=qlen(q);
222*7dd7cddfSDavid du Colombier if(l<1e-12){
223*7dd7cddfSDavid du Colombier q.r=r.i;
224*7dd7cddfSDavid du Colombier q.i=-r.r;
225*7dd7cddfSDavid du Colombier q.j=r.k;
226*7dd7cddfSDavid du Colombier q.k=-r.j;
227*7dd7cddfSDavid du Colombier }
228*7dd7cddfSDavid du Colombier else{
229*7dd7cddfSDavid du Colombier q.r/=l;
230*7dd7cddfSDavid du Colombier q.i/=l;
231*7dd7cddfSDavid du Colombier q.j/=l;
232*7dd7cddfSDavid du Colombier q.k/=l;
233*7dd7cddfSDavid du Colombier }
234*7dd7cddfSDavid du Colombier return q;
235219b2ee8SDavid du Colombier }
236219b2ee8SDavid du Colombier /*
237219b2ee8SDavid du Colombier * Only works if qlen(q)==1
238219b2ee8SDavid du Colombier */
239*7dd7cddfSDavid du Colombier static Quaternion qident={1,0,0,0};
qsqrt(Quaternion q)240219b2ee8SDavid du Colombier Quaternion qsqrt(Quaternion q){
241*7dd7cddfSDavid du Colombier return qmid(q, qident);
242219b2ee8SDavid du Colombier }
243