xref: /plan9/sys/src/libgeometry/qball.c (revision 219b2ee8daee37f4aad58d63f21287faa8e4ffdc)
1 /*
2  * Ken Shoemake's Quaternion rotation controller
3  */
4 #include <u.h>
5 #include <libc.h>
6 #include <libg.h>
7 #include <stdio.h>
8 #include <geometry.h>
9 #define	BORDER	4
10 static Point ctlcen;		/* center of qball */
11 static int ctlrad;		/* radius of qball */
12 static Quaternion *axis;	/* constraint plane orientation, 0 if none */
13 /*
14  * Convert a mouse point into a unit quaternion, flattening if
15  * constrained to a particular plane.
16  */
17 static Quaternion mouseq(Point p){
18 	double qx=(double)(p.x-ctlcen.x)/ctlrad;
19 	double qy=(double)(p.y-ctlcen.y)/ctlrad;
20 	double rsq=qx*qx+qy*qy;
21 	double l;
22 	Quaternion q;
23 	if(rsq>1){
24 		rsq=sqrt(rsq);
25 		q=(Quaternion){0., qx/rsq, qy/rsq, 0.};
26 	}
27 	else
28 		q=(Quaternion){0., qx, qy, sqrt(1.-rsq)};
29 	if(axis){
30 		l=q.i*axis->i+q.j*axis->j+q.k*axis->k;
31 		q.i-=l*axis->i;
32 		q.j-=l*axis->j;
33 		q.k-=l*axis->k;
34 		l=sqrt(q.i*q.i+q.j*q.j+q.k*q.k);
35 		if(l!=0.){
36 			q.i/=l;
37 			q.j/=l;
38 			q.k/=l;
39 		}
40 	}
41 	return q;
42 }
43 void qball(Rectangle r, Mouse *m, Quaternion *result, void (*redraw)(void), Quaternion *ap){
44 	Quaternion q, down;
45 	Point rad;
46 	axis=ap;
47 	ctlcen=div(add(r.min, r.max), 2);
48 	rad=div(sub(r.max, r.min), 2);
49 	ctlrad=(rad.x<rad.y?rad.x:rad.y)-BORDER;
50 	down=qinv(mouseq(m->xy));
51 	q=*result;
52 	for(;;){
53 		*m=emouse();
54 		if(!m->buttons) break;
55 		*result=qmul(q, qmul(down, mouseq(m->xy)));
56 		(*redraw)();
57 	}
58 }
59