/*
* Quaternion arithmetic:
* qadd(q, r) returns q+r
* qsub(q, r) returns q-r
* qneg(q) returns -q
* qmul(q, r) returns q*r
* qdiv(q, r) returns q/r, can divide check.
* qinv(q) returns 1/q, can divide check.
* double qlen(p) returns modulus of p
* qunit(q) returns a unit quaternion parallel to q
* The following only work on unit quaternions and rotation matrices:
* slerp(q, r, a) returns q*(r*q^-1)^a
* qmid(q, r) slerp(q, r, .5)
* qsqrt(q) qmid(q, (Quaternion){1,0,0,0})
* qtom(m, q) converts a unit quaternion q into a rotation matrix m
* mtoq(m) returns a quaternion equivalent to a rotation matrix m
*/
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <geometry.h>
void qtom(Matrix m, Quaternion q){
#ifndef new
m[0][0]=1-2*(q.j*q.j+q.k*q.k);
m[0][1]=2*(q.i*q.j+q.r*q.k);
m[0][2]=2*(q.i*q.k-q.r*q.j);
m[0][3]=0;
m[1][0]=2*(q.i*q.j-q.r*q.k);
m[1][1]=1-2*(q.i*q.i+q.k*q.k);
m[1][2]=2*(q.j*q.k+q.r*q.i);
m[1][3]=0;
m[2][0]=2*(q.i*q.k+q.r*q.j);
m[2][1]=2*(q.j*q.k-q.r*q.i);
m[2][2]=1-2*(q.i*q.i+q.j*q.j);
m[2][3]=0;
m[3][0]=0;
m[3][1]=0;
m[3][2]=0;
m[3][3]=1;
#else
/*
* Transcribed from Ken Shoemake's new code -- not known to work
*/
double Nq = q.r*q.r+q.i*q.i+q.j*q.j+q.k*q.k;
double s = (Nq > 0.0) ? (2.0 / Nq) : 0.0;
double xs = q.i*s, ys = q.j*s, zs = q.k*s;
double wx = q.r*xs, wy = q.r*ys, wz = q.r*zs;
double xx = q.i*xs, xy = q.i*ys, xz = q.i*zs;
double yy = q.j*ys, yz = q.j*zs, zz = q.k*zs;
m[0][0] = 1.0 - (yy + zz); m[1][0] = xy + wz; m[2][0] = xz - wy;
m[0][1] = xy - wz; m[1][1] = 1.0 - (xx + zz); m[2][1] = yz + wx;
m[0][2] = xz + wy; m[1][2] = yz - wx; m[2][2] = 1.0 - (xx + yy);
m[0][3] = m[1][3] = m[2][3] = m[3][0] = m[3][1] = m[3][2] = 0.0;
m[3][3] = 1.0;
#endif
}
Quaternion mtoq(Matrix mat){
#ifndef new
#define EPS 1.387778780781445675529539585113525e-17 /* 2^-56 */
double t;
Quaternion q;
q.r=0.;
q.i=0.;
q.j=0.;
q.k=1.;
if((t=.25*(1+mat[0][0]+mat[1][1]+mat[2][2]))>EPS){
q.r=sqrt(t);
t=4*q.r;
q.i=(mat[1][2]-mat[2][1])/t;
q.j=(mat[2][0]-mat[0][2])/t;
q.k=(mat[0][1]-mat[1][0])/t;
}
else if((t=-.5*(mat[1][1]+mat[2][2]))>EPS){
q.i=sqrt(t);
t=2*q.i;
q.j=mat[0][1]/t;
q.k=mat[0][2]/t;
}
else if((t=.5*(1-mat[2][2]))>EPS){
q.j=sqrt(t);
q.k=mat[1][2]/(2*q.j);
}
return q;
#else
/*
* Transcribed from Ken Shoemake's new code -- not known to work
*/
/* This algorithm avoids near-zero divides by looking for a large
* component -- first r, then i, j, or k. When the trace is greater than zero,
* |r| is greater than 1/2, which is as small as a largest component can be.
* Otherwise, the largest diagonal entry corresponds to the largest of |i|,
* |j|, or |k|, one of which must be larger than |r|, and at least 1/2.
*/
Quaternion qu;
double tr, s;