chiark / gitweb /
remove dead wood; reenable graph_layout_cost
[moebius2.git] / view.c
diff --git a/view.c b/view.c
index abecc7ca4d5fc206532f00d3f7c33dfe41281b78..d2650113e0d00f98d703c67928f2aa072a6ea0b6 100644 (file)
--- a/view.c
+++ b/view.c
@@ -54,6 +54,8 @@ static void transform_coordinates(void) {
   }
 }
 
+static int vertex_in_triangles[N], vertex_in_triangles_checked;
+
 static void addtriangle(int va, int vb, int vc) {
   Triangle *t= &trisbuffer[ntris];
   int k;
@@ -64,30 +66,51 @@ static void addtriangle(int va, int vb, int vc) {
     t->vertex[1][k]= conformation[vb][k];
     t->vertex[2][k]= conformation[vc][k];
   }
+  if (!vertex_in_triangles_checked) {
+    vertex_in_triangles[va]++;
+    vertex_in_triangles[vb]++;
+    vertex_in_triangles[vc]++;
+  }
   displaylist[ntris++]= t;
 }
 
 static void generate_display_list(void) {
-  int vb, ve[3], e;
+  int vb, ve[V6], e;
 
   ntris= 0;
   FOR_VERTEX(vb) {
-    /* We use the two triangles in the parallelogram vb, vb+e0, vb+e1, vb+e2.
+    /* We use the two triangles in the parallelogram vb, vb+e5, vb+e0, vb+e1.
      * We go round each triangle clockwise (although our surface is non-
-     * orientable so it shouldn't matter).
+     * orientable so it shouldn't matter).  Picking the parallelogram
+     * to our right avoids getting it wrong at the join.
      */
-    for (e=0; e<3; e++) ve[e]= EDGE_END2(vb,e);
-    if (ve[1]>=0) {
-      if (ve[0]>=0) addtriangle(vb,ve[0],ve[1]);
-      if (ve[2]>=0) addtriangle(vb,ve[1],ve[2]);
+//if ((vb & YMASK) > Y1) continue;
+//if ((vb & XMASK) > 2) continue; 
+    for (e=0; e<V6; e++) ve[e]= EDGE_END2(vb,e);
+    assert(ve[0]>=0);
+    if (ve[5]>=0) addtriangle(vb,ve[0],ve[5]);
+//continue;
+    if (ve[1]>=0) addtriangle(vb,ve[1],ve[0]);
+  }
+
+  if (!vertex_in_triangles_checked) {
+    int v, expd;
+    FOR_VERTEX(v) {
+      expd= RIM_VERTEX_P(v) ? 3 : 6;
+      if (vertex_in_triangles[v] != expd) {
+       fprintf(stderr,"vertex %02x used for %d triangles, expected %d\n",
+               v, vertex_in_triangles[v], expd);
+//     abort();
+      }
     }
+    vertex_in_triangles_checked= 1;
   }
 }    
 
 static int dl_compare(const void *tav, const void *tbv) {
   int i;
   const Triangle *const *tap= tav, *ta= *tap;
-  const Triangle *const *tbp= tbp, *tb= *tbp;
+  const Triangle *const *tbp= tbv, *tb= *tbp;
   double za=0, zb=0;
   for (i=0; i<3; i++) {
     za += ta->vertex[i][2];
@@ -114,19 +137,21 @@ static Window window;
 static DrawingMode dmred, dmblue, dmwhite;
 static const DrawingMode *dmcurrent;
 static int wwidth=WSZ, wheight=WSZ, wmindim=WSZ, wmaxdim=WSZ;
-static int ncut, currentbuffer, x11depth, x11screen;
+static int ncut, currentbuffer, x11depth, x11screen, wireframe;
 XVisualInfo visinfo;
 
 static double sizeadj_scale= 0.3, eyes_apart, scale_wmindim;
 static double eye_z= -10, eye_x=0;
 static double cut_z= -9;
+static const double eyes_apart_preferred=0.05, eyes_apart_min= -0.02;
+
 
 static void drawtriangle(const Triangle *t) {
   XPoint points[4];
   int i;
 
   for (i=0; i<3; i++) {
-    double *v= t->vertex[i];
+    const double *v= t->vertex[i];
     double x= v[0];
     double y= v[1];
     double z= v[2];
@@ -139,14 +164,16 @@ static void drawtriangle(const Triangle *t) {
   }
   points[3]= points[0];
 
-  XA( XFillPolygon(display,pixmap, dmcurrent->fillgc,
-                  points,3,Convex,CoordModeOrigin) );
+  if (!wireframe)
+    XA( XFillPolygon(display,pixmap, dmcurrent->fillgc,
+                    points,3,Convex,CoordModeOrigin) );
   XA( XDrawLines(display,pixmap, dmcurrent->linegc,
                 points, 4,CoordModeOrigin) );
 }
 
 static const unsigned long core_event_mask=
-  ButtonPressMask|ButtonReleaseMask|StructureNotifyMask|ButtonMotionMask;
+  ButtonPressMask|ButtonReleaseMask|StructureNotifyMask|ButtonMotionMask|
+  KeyPressMask;
 
 static void mkpixmaps(void) {
   for (currentbuffer=0; currentbuffer<2; currentbuffer++) {
@@ -214,11 +241,13 @@ static void display_conformation(void) {
   XA( XFillRectangle(display,pixmap,dmwhite.fillgc,0,0,wwidth,wheight) );
 
   if (eyes_apart > 0) {
-    const double preferred=0.05, beyond=0.07;
+    const double stationary= 0.07;
 
-    eye_x= eyes_apart < preferred ? eyes_apart :
-           eyes_apart < beyond ? preferred :
-           eyes_apart - (beyond - preferred);
+    eye_x= eyes_apart < eyes_apart_preferred
+              ? eyes_apart :
+           eyes_apart < (eyes_apart_preferred + stationary)
+              ? eyes_apart_preferred
+              : eyes_apart - stationary;
     eye_x /= sizeadj_scale;
     drawtriangles(&dmblue);
     eye_x= -eye_x;
@@ -244,7 +273,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);
@@ -256,10 +285,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) {               \
@@ -268,7 +298,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) { }
@@ -291,6 +321,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:
    *
@@ -305,31 +354,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;
@@ -337,26 +411,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);
 
@@ -398,22 +479,21 @@ 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) {
-  const double min_eyes_apart= -0.02;
   eyes_apart += dx * 0.1;
-  if (eyes_apart < min_eyes_apart) eyes_apart= min_eyes_apart;
+  if (eyes_apart < eyes_apart_min) eyes_apart= eyes_apart_min;
   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;
 
@@ -446,7 +526,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);
@@ -461,6 +541,31 @@ static void event_motion(int x, int y) {
   drag_position(x,y);
 }
 
+static void event_key(XKeyEvent *e) {
+  KeySym ks;
+  char buf[10];
+  int r;
+
+  r= XLookupString(e,buf,sizeof(buf)-1,&ks,0);
+  if (!r) {
+    printf("XLookupString keycode=%u state=0x%x gave %d\n",
+          e->keycode, e->state, r);
+    return;
+  }
+
+  if (!strcmp(buf,"q")) exit(0);
+  if (!strcmp(buf,"w")) {
+    wireframe= !wireframe;
+    show();
+    return;
+  }
+  if (!strcmp(buf,"d")) {
+    eyes_apart= eyes_apart>0 ? eyes_apart_min : eyes_apart_preferred;
+    show();
+    return;
+  }
+}
+
 static void event_config(XConfigureEvent *e) {
   if (e->width == wwidth && e->height == wheight)
     return;
@@ -496,6 +601,23 @@ static void check_input(void) {
   show();
 }
 
+static void topocheck(void) {
+  int v1,e,v2,eprime,v1prime, count;
+  FOR_EDGE(v1,e,v2) {
+    count= 0;
+    FOR_VEDGE(v2,eprime,v1prime)
+      if (v1prime==v1) count++;
+    if (count!=1) {
+      fprintf(stderr,"%02x -%d-> %02x  reverse edge count = %d!\n",
+             v1,e,v2, count);
+      FOR_VEDGE(v2,eprime,v1prime)
+       fprintf(stderr,"%02x -%d-> %02x -> %d -> %02x\n",
+               v1,e,v2,eprime,v1prime);
+      exit(-1);
+    }
+  }
+}
+
 int main(int argc, const char *const *argv) {
   static const int wantedevents= POLLIN|POLLPRI|POLLERR|POLLHUP;
 
@@ -503,6 +625,9 @@ int main(int argc, const char *const *argv) {
   int k, i, r, *xfds, nxfds, polls_alloc=0;
   struct pollfd *polls=0;
   int motion_deferred=0, motion_x=-1, motion_y=-1;
+
+  topocheck();
+  if (argc==1) { printf("topology self-consistent, ok\n"); exit(0); }
   
   if (argc != 2 || argv[1][0]=='-') {
     fputs("need filename\n",stderr); exit(8);
@@ -555,6 +680,8 @@ int main(int argc, const char *const *argv) {
     case ButtonPress:
     case ButtonRelease:     event_button(&event.xbutton);     break;
       
+    case KeyPress:          event_key(&event.xkey);           break;
+
     case ConfigureNotify:   event_config(&event.xconfigure);  break;
 
     case MotionNotify: