1 /*
2 * Ken Shoemake's Quaternion rotation controller
3 */
4 #include <u.h>
5 #include <libc.h>
6 #include <draw.h>
7 #include <event.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 */
mouseq(Point p)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.r=0.;
26 q.i=qx/rsq;
27 q.j=qy/rsq;
28 q.k=0.;
29 }
30 else{
31 q.r=0.;
32 q.i=qx;
33 q.j=qy;
34 q.k=sqrt(1.-rsq);
35 }
36 if(axis){
37 l=q.i*axis->i+q.j*axis->j+q.k*axis->k;
38 q.i-=l*axis->i;
39 q.j-=l*axis->j;
40 q.k-=l*axis->k;
41 l=sqrt(q.i*q.i+q.j*q.j+q.k*q.k);
42 if(l!=0.){
43 q.i/=l;
44 q.j/=l;
45 q.k/=l;
46 }
47 }
48 return q;
49 }
qball(Rectangle r,Mouse * m,Quaternion * result,void (* redraw)(void),Quaternion * ap)50 void qball(Rectangle r, Mouse *m, Quaternion *result, void (*redraw)(void), Quaternion *ap){
51 Quaternion q, down;
52 Point rad;
53 axis=ap;
54 ctlcen=divpt(addpt(r.min, r.max), 2);
55 rad=divpt(subpt(r.max, r.min), 2);
56 ctlrad=(rad.x<rad.y?rad.x:rad.y)-BORDER;
57 down=qinv(mouseq(m->xy));
58 q=*result;
59 for(;;){
60 *m=emouse();
61 if(!m->buttons) break;
62 *result=qmul(q, qmul(down, mouseq(m->xy)));
63 (*redraw)();
64 }
65 }
66