X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?p=moebius2.git;a=blobdiff_plain;f=view.c;h=32ae7d7df8a8a39ecd9ad0785f0d2870bed54c89;hp=1ce6db66d492867a869ed833b2a9e9c6931067a8;hb=cf1b738d035a22d539a44ca50925fb23cdb7588c;hpb=512fa4d69efa3f71c61a581c137ee6da2a9bf2f9 diff --git a/view.c b/view.c index 1ce6db6..32ae7d7 100644 --- a/view.c +++ b/view.c @@ -5,6 +5,8 @@ #include #include +#include + #include "mgraph.h" #define MAXTRIS (N*2) @@ -18,16 +20,22 @@ static Vertices conformation; static double transform[D3][D3]= {{1,0,0}, {0,1,0}, {0,0,1}}; static GSL_MATRIX(transform); -const char *input_filename; +static FILE *input_f; +static struct stat input_stab; +static const char *input_filename; +static int pause_updates; static void read_input(void) { - FILE *f; int r; - f= fopen(input_filename, "rb"); if (!f) diee("input file"); + if (input_f) fclose(input_f); + input_f= fopen(input_filename, "rb"); if (!input_f) diee("input file"); + + if (fstat(fileno(input_f), &input_stab)) diee("fstat input file"); + errno= 0; - r= fread(&conformation,sizeof(conformation),1,f); if (r!=1) diee("fread"); - fclose(f); + r= fread(&conformation,sizeof(conformation),1,input_f); + if (r!=1) diee("fread"); } static void transform_coordinates(void) { @@ -45,6 +53,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; @@ -55,30 +65,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=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]; @@ -105,19 +136,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]; @@ -130,14 +163,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|SubstructureNotifyMask; static void mkpixmaps(void) { for (currentbuffer=0; currentbuffer<2; currentbuffer++) { @@ -205,11 +240,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; @@ -235,7 +272,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); @@ -247,10 +284,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) { \ @@ -259,7 +297,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) { } @@ -282,6 +320,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: * @@ -296,31 +353,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' = Z R^T X R T where + * or T' = Z 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; @@ -328,26 +410,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); @@ -389,22 +478,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; @@ -437,7 +525,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); @@ -452,6 +540,85 @@ static void event_motion(int x, int y) { drag_position(x,y); } +static void transform_preset_record(const char *fn, const char *fn_new) { + FILE *f; + f= fopen(fn_new,"wb"); + if (!f) diee("open new transform"); + if (fwrite(transform,sizeof(transform),1,f) != 1) diee("write transform"); + if (fclose(f)) diee("fclose new transform"); + if (rename(fn_new,fn)) diee("install transform"); +} + +static void transform_preset_playback(const char *fn) { + FILE *f; + f= fopen(fn,"rb"); + if (!f && errno==ENOENT) { + fprintf(stderr,"no preset %s\n",fn); + XBell(display,100); + return; + } + errno= 0; + if (fread(transform,sizeof(transform),1,f) != 1) { + perror("read preset!"); + XBell(display,100); + return; + } + fclose(f); + show(); +} + +static void event_key(XKeyEvent *e) { + KeySym ks; + XKeyEvent e_nomod; + char buf[10], buf_nomod[10]; + int r, r_nomod; + + 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); + else if (!strcmp(buf,"p")) + pause_updates= !pause_updates; + else if (!strcmp(buf,"w")) { + wireframe= !wireframe; + show(); + return; + } else if (!strcmp(buf,"d")) { + eyes_apart= eyes_apart>0 ? eyes_apart_min : eyes_apart_preferred; + show(); + return; + } + + e_nomod= *e; + e_nomod.state= 0; + buf_nomod[0]= 0; + r_nomod= XLookupString(&e_nomod,buf_nomod,sizeof(buf_nomod)-1,&ks,0); + if (r_nomod && !buf_nomod[1] && buf_nomod[0]>='0' && buf_nomod[0]<='9') { + char filename[20], filename_new[25]; + snprintf(filename,sizeof(filename)-1,".view-preset-%s",buf_nomod); + snprintf(filename_new,sizeof(filename_new)-1,"%s.new",filename); + printf("transform preset %d %s\n", e->state, filename); + if (e->state) transform_preset_record(filename,filename_new); + else transform_preset_playback(filename); + return; + } + + printf("unknown key keycode=%d state=0x%x char=%c 0x%02x " + "[rnm=%d bnm[0,1]=0x%02x,%02x]\n", + e->keycode, e->state, buf[0]>' ' && buf[0]<127 ? buf[0] : '?', + buf[0], r_nomod, buf_nomod[0], buf_nomod[1]); + printf("%d %d %d %d\n", + r_nomod, + !buf_nomod[1], + buf_nomod[0]>='0', + buf_nomod[0]<='9'); +} + static void event_config(XConfigureEvent *e) { if (e->width == wwidth && e->height == wheight) return; @@ -468,10 +635,52 @@ static void event_config(XConfigureEvent *e) { show(); } +static void check_input(void) { + struct stat newstab; + int r; + + r= stat(input_filename, &newstab); + if (r<0) diee("could not check input"); + +#define CI(x) if (newstab.st_##x == input_stab.st_##x) ; else goto changed + CI(dev); + CI(ino); + CI(size); + CI(mtime); +#undef CI + return; + + changed: + 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; + XEvent event; - int k; + 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); @@ -485,21 +694,51 @@ int main(int argc, const char *const *argv) { XMapWindow(display,window); for (;;) { - if (motion_deferred) { - int r= XCheckMaskEvent(display,~0UL,&event); - if (!r) { + + XA( XInternalConnectionNumbers(display, &xfds, &nxfds) ); + if (polls_alloc <= nxfds) { + polls_alloc= nxfds + polls_alloc + 1; + polls= realloc(polls, sizeof(*polls) * polls_alloc); + if (!polls) diee("realloc for pollfds"); + } + for (i=0; i