qball.c 1.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
  1. /*
  2. * This file is part of the UCB release of Plan 9. It is subject to the license
  3. * terms in the LICENSE file found in the top-level directory of this
  4. * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
  5. * part of the UCB release of Plan 9, including this file, may be copied,
  6. * modified, propagated, or distributed except according to the terms contained
  7. * in the LICENSE file.
  8. */
  9. /*
  10. * Ken Shoemake's Quaternion rotation controller
  11. */
  12. #include <u.h>
  13. #include <libc.h>
  14. #include <draw.h>
  15. #include <event.h>
  16. #include <geometry.h>
  17. #define BORDER 4
  18. static Point ctlcen; /* center of qball */
  19. static int ctlrad; /* radius of qball */
  20. static Quaternion *axis; /* constraint plane orientation, 0 if none */
  21. /*
  22. * Convert a mouse point into a unit quaternion, flattening if
  23. * constrained to a particular plane.
  24. */
  25. static Quaternion mouseq(Point p){
  26. double qx=(double)(p.x-ctlcen.x)/ctlrad;
  27. double qy=(double)(p.y-ctlcen.y)/ctlrad;
  28. double rsq=qx*qx+qy*qy;
  29. double l;
  30. Quaternion q;
  31. if(rsq>1){
  32. rsq=sqrt(rsq);
  33. q.r=0.;
  34. q.i=qx/rsq;
  35. q.j=qy/rsq;
  36. q.k=0.;
  37. }
  38. else{
  39. q.r=0.;
  40. q.i=qx;
  41. q.j=qy;
  42. q.k=sqrt(1.-rsq);
  43. }
  44. if(axis){
  45. l=q.i*axis->i+q.j*axis->j+q.k*axis->k;
  46. q.i-=l*axis->i;
  47. q.j-=l*axis->j;
  48. q.k-=l*axis->k;
  49. l=sqrt(q.i*q.i+q.j*q.j+q.k*q.k);
  50. if(l!=0.){
  51. q.i/=l;
  52. q.j/=l;
  53. q.k/=l;
  54. }
  55. }
  56. return q;
  57. }
  58. void qball(Rectangle r, Mouse *m, Quaternion *result, void (*redraw)(void), Quaternion *ap){
  59. Quaternion q, down;
  60. Point rad;
  61. axis=ap;
  62. ctlcen=divpt(addpt(r.min, r.max), 2);
  63. rad=divpt(subpt(r.max, r.min), 2);
  64. ctlrad=(rad.x<rad.y?rad.x:rad.y)-BORDER;
  65. down=qinv(mouseq(m->xy));
  66. q=*result;
  67. for(;;){
  68. *m=emouse();
  69. if(!m->buttons) break;
  70. *result=qmul(q, qmul(down, mouseq(m->xy)));
  71. (*redraw)();
  72. }
  73. }