]> git.lizzy.rs Git - plan9front.git/blob - sys/src/libgeometry/qball.c
libaml: fix gc bug, need to amltake()/amldrop() temporary buffer
[plan9front.git] / sys / src / libgeometry / qball.c
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  */
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 }
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 }