chiark / gitweb /
better rotations
authorIan Jackson <ian@davenant.relativity.greenend.org.uk>
Mon, 31 Dec 2007 17:03:41 +0000 (17:03 +0000)
committerIan Jackson <ian@davenant.relativity.greenend.org.uk>
Mon, 31 Dec 2007 17:03:41 +0000 (17:03 +0000)
common.h
view.c

index cb45e81..ebc34d1 100644 (file)
--- a/common.h
+++ b/common.h
@@ -50,6 +50,9 @@ void gsldie(int l, const char *what, int status);
 #define GSL_VECTOR(x) gsl_vector x##_gsl= { D3,1,&x[0] };
 #define GSL_MATRIX(x) gsl_matrix x##_gsl= { D3,D3,D3,&x[0][0] };
 
+#define MIN(a,b) ((a) <= (b) ? (a) : (b))
+#define MAX(a,b) ((a) >= (b) ? (a) : (b))
+
 #ifdef FP_FAST_FMA
 # define fma_fast fma
 #else
diff --git a/view.c b/view.c
index 712623c..e269e25 100644 (file)
--- a/view.c
+++ b/view.c
@@ -249,7 +249,7 @@ static void show(void) {
 
 typedef struct {
   const char *name;
-  void (*start)(void);
+  void (*start)(const XButtonEvent *e);
   void (*delta)(double dx, double dy);
   void (*conclude)(void);
   void (*abandon)(void);
@@ -261,10 +261,11 @@ typedef struct {
     drag_##x##_conclude, drag_##x##_abandon    \
   }
 
-#define DRAG_SAVING(x, thing)                          \
+#define DRAG_SAVING(x, thing, hook)                    \
   static typeof(thing) original_##thing;               \
-  static void drag_##x##_start(void) {                 \
+  static void drag_##x##_start(const XButtonEvent *e) {        \
     memcpy(&original_##thing, &thing, sizeof(thing));  \
+    hook;                                              \
   }                                                    \
   static void drag_##x##_conclude(void) { }            \
   static void drag_##x##_abandon(void) {               \
@@ -273,7 +274,7 @@ typedef struct {
   }                                                    \
   DRAG(x)
 
-static void drag_none_start(void) { }
+static void drag_none_start(const XButtonEvent *e) { }
 static void drag_none_delta(double dx, double dy) { }
 static void drag_none_conclude(void) { }
 static void drag_none_abandon(void) { }
@@ -296,6 +297,25 @@ static void pmatrix(const char *n, double m[D3][D3]) {
 }
 #define PMATRIX(x) pmatrix(#x,x);
 
+static double drag_transform_conv_x_z= 0;
+static double drag_transform_conv_y_z= 0;
+
+static void drag_transform_prep(const XButtonEvent *e) {
+  static const double factor= 2.5;
+  drag_transform_conv_x_z= MAX( MIN(e->y * factor / wheight - (factor/2),
+                                   1.0), -1.0);
+  drag_transform_conv_y_z= MAX( MIN(e->x * factor / wwidth  - (factor/2),
+                                   1.0), -1.0);
+  printf("drag_transform_conv_{x,y}_z = %g,%g\n",
+        drag_transform_conv_x_z, drag_transform_conv_y_z);
+}
+
+static void make_z_rotation(double rotz[D3][D3], double cz, double sz) {
+  rotz[0][0]=  cz;    rotz[0][1]=  sz;     rotz[0][2]=  0;
+  rotz[1][0]= -sz;    rotz[1][1]=  cz;     rotz[1][2]=  0;
+  rotz[2][0]=   0;    rotz[2][1]=   0;     rotz[2][2]=  1;
+}  
+
 static void drag_rotate_delta(double dx, double dy) {
   /* We multiple our transformation matrix by a matrix:
    *
@@ -310,31 +330,56 @@ static void drag_rotate_delta(double dx, double dy) {
    * we make cy and sy be cos() and sin(hypot(x,y)) and use
    * with cr,sr as cos() and sin(atan2(y,y)):
    *
-   * Ie we would do  T' = R^T X R T   where
-   *             or  T' =    C    T   where  C = R^T X R  and
+   * Ie we would do  T' = R^T X R T   where
+   *             or  T' =    C    T   where  C = R^T X R  and
    *
    *  adjustment R = [  cr  sr  0 ]
    *                 [ -sr  cr  0 ]
-   *                 [  0    0  1 ]
+   *                 [  0    0  1 ]    or make_z_rotation(cr,sr)
+   *
+   *  rotation Z   = [  cz  sz  0 ]
+   *                 [ -sz  cz  0 ]
+   *                 [  0    0  1 ]    or make_z_rotation(cz,sz)
    */
 
-  double rotx[D3][D3], adjr[D3][D3];
+  double rotx[D3][D3], adjr[D3][D3], rotz[D3][D3];
   GSL_MATRIX(rotx);
   GSL_MATRIX(adjr);
+  GSL_MATRIX(rotz);
 
   static double temp[D3][D3], change[D3][D3];
   static GSL_MATRIX(temp);
   static GSL_MATRIX(change);
 
+  printf("\nTRANSFORM %g, %g\n", dx,dy);
+
+  double dz= -drag_transform_conv_x_z * dx +
+              drag_transform_conv_y_z * dy;
+         
+  dx *= (1 - fabs(drag_transform_conv_x_z));
+  dy *= (1 - fabs(drag_transform_conv_y_z));
+
   double d= hypot(dx,dy);
-  if (d < 1e-6) return;
+
+  printf(" dx,dy,dz = %g, %g, %g    d = %g\n", dx,dy,dz, d);
+
+  if (hypot(d,dz) < 1e-6) return;
+
+  printf(" no xy rotation\n");
 
   double ang= d*2.0;
   
   double cy= cos(ang);
   double sy= sin(ang);
-  double cr= -dy / d;
-  double sr=  dx / d;
+
+  double cr, sr;
+  if (d > 1e-6) {
+    cr= -dy / d;
+    sr=  dx / d;
+  } else {
+    cr= 1;
+    sr= 0;
+  }
   printf("\n d=%g cy,sy=%g,%g cr,sr=%g,%g\n\n", d,cy,sy,cr,sr);
   
   rotx[0][0]=   1;    rotx[0][1]=   0;     rotx[0][2]=  0;
@@ -342,26 +387,33 @@ static void drag_rotate_delta(double dx, double dy) {
   rotx[2][0]=   0;    rotx[2][1]= -sy;     rotx[2][2]= cy;
   PMATRIX(rotx);
 
-  adjr[0][0]=  cr;    adjr[0][1]=  sr;     adjr[0][2]=  0;
-  adjr[1][0]= -sr;    adjr[1][1]=  cr;     adjr[1][2]=  0;
-  adjr[2][0]=   0;    adjr[2][1]=   0;     adjr[2][2]=  1;
+  make_z_rotation(adjr,cr,sr);
   PMATRIX(adjr);
 
   GA( gsl_blas_dgemm(CblasNoTrans,CblasNoTrans, 1.0,
                     &rotx_gsl,&adjr_gsl,
                     0.0, &temp_gsl) );
-  PMATRIX(temp);
+  pmatrix("X R", temp);
   
   GA( gsl_blas_dgemm(CblasTrans,CblasNoTrans, 1.0,
                     &adjr_gsl,&temp_gsl,
                     0.0, &change_gsl) );
   PMATRIX(change);
 
+  double angz= dz*2.0;
+  make_z_rotation(rotz,cos(angz),sin(angz));
+  PMATRIX(rotz);
+
+  GA( gsl_blas_dgemm(CblasTrans,CblasNoTrans, 1.0,
+                    &rotz_gsl,&change_gsl,
+                    0.0, &temp_gsl) );
+  pmatrix("Z C", temp);
+
   static double skew[D3][D3];
   static GSL_MATRIX(skew);
   
   GA( gsl_blas_dgemm(CblasNoTrans,CblasNoTrans, 1.0,
-                    &change_gsl,&transform_gsl,
+                    &temp_gsl, &transform_gsl,
                     0.0, &skew_gsl) );
   PMATRIX(skew);
 
@@ -403,13 +455,13 @@ static void drag_rotate_delta(double dx, double dy) {
   printf("drag_rotate_delta...\n");
   show();
 }
-DRAG_SAVING(rotate, transform);
+DRAG_SAVING(rotate, transform, drag_transform_prep(e));
 
 static void drag_sizeadj_delta(double dx, double dy) {
   sizeadj_scale *= pow(3.0, -dy);
   show();
 }
-DRAG_SAVING(sizeadj, sizeadj_scale);
+DRAG_SAVING(sizeadj, sizeadj_scale);
 
 static void drag_3d_delta(double dx, double dy) {
   eyes_apart += dx * 0.1;
@@ -417,7 +469,7 @@ static void drag_3d_delta(double dx, double dy) {
   printf("sizeadj eyes_apart %g\n", eyes_apart);
   show();
 }
-DRAG_SAVING(3d, eyes_apart);
+DRAG_SAVING(3d, eyes_apart);
 
 static const Drag *drag= &drag_none;
 
@@ -450,7 +502,7 @@ static void event_button(XButtonEvent *e) {
           drag->name, (unsigned long)e->button, e->x, e->y);
     drag_last_x= e->x;
     drag_last_y= e->y;
-    drag->start();
+    drag->start(e);
   }
   if (e->type == ButtonRelease) {
     printf("drag=%s release %d,%d\n", drag->name, e->x, e->y);