| /* | 
 |  * Ken Shoemake's Quaternion rotation controller | 
 |  */ | 
 | #include <u.h> | 
 | #include <libc.h> | 
 | #include <draw.h> | 
 | #include <stdio.h> | 
 | #include <event.h> | 
 | #include <geometry.h> | 
 | #define	BORDER	4 | 
 | static Point ctlcen;		/* center of qball */ | 
 | static int ctlrad;		/* radius of qball */ | 
 | static Quaternion *axis;	/* constraint plane orientation, 0 if none */ | 
 | /* | 
 |  * Convert a mouse point into a unit quaternion, flattening if | 
 |  * constrained to a particular plane. | 
 |  */ | 
 | static Quaternion mouseq(Point p){ | 
 | 	double qx=(double)(p.x-ctlcen.x)/ctlrad; | 
 | 	double qy=(double)(p.y-ctlcen.y)/ctlrad; | 
 | 	double rsq=qx*qx+qy*qy; | 
 | 	double l; | 
 | 	Quaternion q; | 
 | 	if(rsq>1){ | 
 | 		rsq=sqrt(rsq); | 
 | 		q.r=0.; | 
 | 		q.i=qx/rsq; | 
 | 		q.j=qy/rsq; | 
 | 		q.k=0.; | 
 | 	} | 
 | 	else{ | 
 | 		q.r=0.; | 
 | 		q.i=qx; | 
 | 		q.j=qy; | 
 | 		q.k=sqrt(1.-rsq); | 
 | 	} | 
 | 	if(axis){ | 
 | 		l=q.i*axis->i+q.j*axis->j+q.k*axis->k; | 
 | 		q.i-=l*axis->i; | 
 | 		q.j-=l*axis->j; | 
 | 		q.k-=l*axis->k; | 
 | 		l=sqrt(q.i*q.i+q.j*q.j+q.k*q.k); | 
 | 		if(l!=0.){ | 
 | 			q.i/=l; | 
 | 			q.j/=l; | 
 | 			q.k/=l; | 
 | 		} | 
 | 	} | 
 | 	return q; | 
 | } | 
 | void qball(Rectangle r, Mouse *m, Quaternion *result, void (*redraw)(void), Quaternion *ap){ | 
 | 	Quaternion q, down; | 
 | 	Point rad; | 
 | 	axis=ap; | 
 | 	ctlcen=divpt(addpt(r.min, r.max), 2); | 
 | 	rad=divpt(subpt(r.max, r.min), 2); | 
 | 	ctlrad=(rad.x<rad.y?rad.x:rad.y)-BORDER; | 
 | 	down=qinv(mouseq(m->xy)); | 
 | 	q=*result; | 
 | 	for(;;){ | 
 | 		*m=emouse(); | 
 | 		if(!m->buttons) break; | 
 | 		*result=qmul(q, qmul(down, mouseq(m->xy))); | 
 | 		(*redraw)(); | 
 | 	} | 
 | } |