plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

quaternion.c (5705B)


      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 }