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