/***************************************/ /* Window 2.0 */ /* */ /* Threaded X Window 3D Object Group */ /* Animator inspired by Display 1.0. */ /* */ /* Ju Li 1998 MIT */ /***************************************/ #include "W.h" #include "P.h" /* Postscript driver */ #define DBLE_BUFFER (6) /* check if input b of type A in fact comes from */ /* key C; no good for Bool or pointer variables. */ #define key_verify(A,b,C) (b==(A)C) /* see if a point is inside a rectangular box */ #define inside_box(width,height,x,y) \ (((x)*(x-(width))<=W_TINY)&&((y)*(y-(height))<=W_TINY)) #define setlcid(iw,cid) setfg(iw,cid);setLA(iw,cid) #define min(x,y) ((x)<(y)?(x):(y)) #define norm(x,y) ((x)*(x)+(y)*(y)) #define d_token(iw) \ ((order[iw]==W_ORDER_BY_Z)?fabs(eye[iw]-va[iw][2]):va[iw][3]) #define d_line(ad,bd) (((ad)+(bd))/2.) #define rcid(cid,intensity) \ ((abs(cid)<=W_MAXCOLOR)?fade_cid(cid,intensity):cid) /* a cid that can pass through buffered draw */ #define safe_cid(cid) \ ((abs(cid)<=W_MAXCOLOR)?copysign(abs(cid)+W_MAXCOLOR,cid):cid) /* object group list structure */ struct OGL { char token[W_MAX_OG_TOKEN_SIZE]; int obj_type; int n; /* number of objects in group */ void *gp; /* group array pointer */ struct OGL *next; }; /** The scope of following is within this code: internal use only **/ /* The following can be modified by the main */ /* thread without notice, thus volatile. */ static volatile double (*H)[3]; static volatile Bool active[W_MAXWINDOW], LockOGL, OGLRendered[W_MAXWINDOW]; /* the only bit needed to be preset for the Animator */ static Bool A_never_entered = True; /* OGL data is entirely independent of the Animator */ static struct OGL oglpool[W_MAX_NUM_OGS] = {0}; /* Individual window counters which are to */ static int lastCI[W_MAXWINDOW], lastLA[W_MAXWINDOW], xpm_counter[W_MAXWINDOW], ps_counter[W_MAXWINDOW], buffer_top[W_MAXWINDOW], nanim[W_MAXWINDOW]; static long last_button_press_time[W_MAXWINDOW]; static double timespent[W_MAXWINDOW]; static Bool drawps[W_MAXWINDOW]; /* be assigned initial values by the code. */ static char display_addr[W_MAXWINDOW][W_MAX_STRLEN], title[W_MAXWINDOW][W_MAX_STRLEN]; static Display *display[W_MAXWINDOW]; static int screen[W_MAXWINDOW]; static Window root[W_MAXWINDOW], windows[W_MAXWINDOW]; static XSizeHints win[W_MAXWINDOW]; static unsigned long foreground[W_MAXWINDOW], background[W_MAXWINDOW]; static GC gc[W_MAXWINDOW]; static Pixmap pixmap[W_MAXWINDOW]; static unsigned int border_width[W_MAXWINDOW], color_depth[W_MAXWINDOW], mask[W_MAXWINDOW]; static Colormap colormap[W_MAXWINDOW]; static XColor color[W_MAXWINDOW][W_MAXCOLOR*W_COLOR_RESOLUTION]; static int order[W_MAXWINDOW], px[W_MAXWINDOW], py[W_MAXWINDOW], eye_aid[W_MAXWINDOW], lx[W_MAXWINDOW], ly[W_MAXWINDOW], pivot[W_MAXWINDOW]; static double unit[W_MAXWINDOW], A[W_MAXWINDOW][3][3], w[W_MAXWINDOW][3], ws[W_MAXWINDOW][3], mgs_size[W_MAXWINDOW], min_d[W_MAXWINDOW], eye[W_MAXWINDOW], eye_thickness_factor[W_MAXWINDOW], plate_thickness[W_MAXWINDOW], rate[W_MAXWINDOW], dws[W_MAXWINDOW][3], va[W_MAXWINDOW][6], factor[W_MAXWINDOW], alpha[W_MAXWINDOW], beta[W_MAXWINDOW], buffer_d[W_MAXWINDOW][W_MAX_BUFFER], buffer[W_MAXWINDOW][DBLE_BUFFER*W_MAX_BUFFER], x[W_MAXWINDOW], y[W_MAXWINDOW]; static Bool PBC[W_MAXWINDOW], front_clipped[W_MAXWINDOW], behind_clipped[W_MAXWINDOW]; /* internal functions */ static void assign (double a[3], double b[3]); static double dot (double a[3], double b[3]); static double normalize (double a[3]); static void cross (double a[3], double b[3], double c[3]); static void SignIn (volatile double H[3][3]); static void signal_handler_thread (void *a); static void SignOut (); static void set_initial_counters (int iw); static void setfg (int iw, int cid); static void setLA (int iw, int cid); static Bool box_clip_line (double x0,double y0,double width,double height,double xx[4]); static double boxcut (double x0, double y0, double z0, double bx, double by, double bd, double x[3], double dx[3]); static Bool window_clip_circle (int iw, double x, double y, double r, Bool filled); static void rw2s (int iw, double rw[3], double s[3]); static void s2rw (int iw, double s[3], double rw[3]); static void s_truncate (double ds[3]); static void va2rw (int iw, double va[3], double rw[3]); static void rw2va (int iw, double rw[3], double va[3]); static Bool s2screen (int iw, double s[3], Bool pbc); static int fade_cid (int orig_cid, double intensity); static void drawline (int iw, int cid, double xx[4]); static void buffered_drawline(int iw, int cid, double xx[4], double d); static void buffered_drawsegs (int iw, int cid, double x[4], double d1, double d2); static void buffered_drawrectangle (int iw, double x0, double y0, double width, double height, int cid, double d); static void drawcircle(int iw, double x, double y, double a, int cid); static void buffered_drawcircle (int iw, double x, double y, double a, int cid, double d); static double lighting(int iw, double d); static int flush_buffer (int iw); static void Render_LINE (int iw, int n, WLINE gp[]); static void Render_POINT (int iw, int n, WPOINT gp[]); static void Render_SPHERE (int iw, int n, WSPHERE gp[]); static Bool translate_window (int iw, int i, double delta); static void rotate (double A[3], double B[3], double W[3]); static void rotate_window_origin (int iw, double start[3], double finish[3], int mpivot); static void rotate_window (int iw, double start[3], double finish[3], int mpivot); static void MGS (double x, double y, double r, double a[3]); static Bool mouse_rotate_window (int iw, int to_x, int to_y, int mpivot); static void axis_rotate_window (int iw, double rc[3], double theta, int mpivot); static int capture_xpm (int iw); static int init_ps (int iw); static void convert_to_ps_color (int iw); static void close_ps (int iw); static void print_status (int iw); static void draw_eye_aids (int iw); static void print_help (int iw); static Bool treatevent (int iw, XEvent *e); static void Animate (int iw); static void thread_start (void *iw_pointer); void assign (double a[3], double b[3]) { b[0] = a[0]; b[1] = a[1]; b[2] = a[2]; return; } /* end assign() */ double dot (double a[3], double b[3]) { return (a[0]*b[0]+a[1]*b[1]+a[2]*b[2]); } /* end dot() */ double normalize (double a[3]) { double length = sqrt(dot(a,a)); if (length<W_TINY) return(length); a[0] /= length; a[1] /= length; a[2] /= length; return (length); } /* end normalize() */ void cross (double a[3], double b[3], double c[3]) { c[0] = a[1]*b[2] - a[2]*b[1]; c[1] = a[2]*b[0] - a[0]*b[2]; c[2] = a[0]*b[1] - a[1]*b[0]; return; } /* end cross() */ /* SignIn() and SignOut() handle animator birth and death */ void SignIn (volatile double h[3][3]) { int i; if ( A_never_entered ) { H = h; printf("\n==============================================\n"); printf("W ver %.1f signing in (Ju Li, liju99@mit.edu).\n", W_VERSION); printf("==============================================\n\n"); A_never_entered = False; for (i=0; i<W_MAXWINDOW; i++) active[i] = False; } return; } /* end SignIn() */ /* thread-safe signal mechanism */ void signal_handler_thread (void *a) { int sig; sigset_t w_signal; /* current working directory */ char cwd[W_MAX_STRLEN]; FILE *file; struct timeval tp; double inittime, endtime; gettimeofday (&tp, NULL); inittime = tp.tv_sec+0.000001*tp.tv_usec; printf("\n=============================================\n"); printf("W ver %.1f signing out. As long as this grave\n", W_VERSION); printf("keeper thread is alive and H and OGL exist,\n"); printf("you can always create a new animator window\n"); printf("by running %% %s/%s.\n", getcwd(cwd,W_MAX_STRLEN-1), W_GRAVEKEEPER); printf("=============================================\n\n"); file = fopen (W_GRAVEKEEPER, "w"); fprintf (file, "kill -%d %d\n", W_SIGNAL, (int)getpid()); /* ticket is self-destructive */ fprintf (file, "rm %s\n", W_GRAVEKEEPER); fclose (file); /* make it executable */ chmod (W_GRAVEKEEPER, S_IRWXU); /* lone grave-keeper waits for signal */ sigemptyset (&w_signal); sigaddset (&w_signal, W_SIGNAL); sigwait (&w_signal, &sig); gettimeofday (&tp, NULL); endtime = tp.tv_sec+0.000001*tp.tv_usec - inittime; printf("\n=============================================\n"); printf("The grave-keeper has slept for %s,\n", earth_time(endtime)); printf("a new window will soon show up from which\n"); printf("more can be created by F3 or F4. gk exits.\n"); printf("=============================================\n\n"); WAddDefaultWindow((double (*)[3])H); /* the grave-keeper goes to grave */ pthread_exit((void *)NULL); } /* end signal_handler_thread() */ void SignOut() { pthread_t signal_handler_thread_ID; if ( !A_never_entered ) { /* fastest lock of other threads also SignOut() */ A_never_entered = True; /* create a single heir to look after business */ pthread_create (&signal_handler_thread_ID, NULL, (void *(*)(void *))signal_handler_thread, NULL); } return; } /* end SignOut() */ /* Initialize counters for individual windows */ void set_initial_counters (int iw) { lastCI[iw] = -1; /* last used color */ lastLA[iw] = 0; /* last used line attribute */ nanim[iw] = 0; /* number of frames rendered */ /* number of .xpm screenshots taken in this window */ xpm_counter[iw] = ps_counter[iw] = 0; /* time spent (+ waiting) in rendering these frames */ timespent[iw] = 0; last_button_press_time[iw] = -W_HUGE; buffer_top[iw] = 0; drawps[iw] = False; return; } /* end set_initial_counters() */ /* Sets foreground color: avoid */ /* redundant requests to the server */ void setfg (int iw, int cid) { int CI = abs(cid)-1; /* CI is the actual color[] index running */ /* from 0 to W_MAXCOLOR*W_COLOR_RESOLUTION-1 */ if ( (CI<0) || (CI>=W_MAXCOLOR*W_COLOR_RESOLUTION) ) { printf ("setfg(): CI = %d out of bound.\n", CI); exit(1); } if ( lastCI[iw] != CI ) { XSetForeground(display[iw], gc[iw], color[iw][CI].pixel); lastCI[iw] = CI; } return; } /* end setfg() */ /* Sets line attribute: >0 solid, <0 dash */ void setLA (int iw, int cid) { if ( lastLA[iw] * cid <= 0 ) { if ( cid > 0 ) XSetLineAttributes (display[iw], gc[iw], 0, LineSolid, CapButt, JoinMiter); else if ( cid < 0 ) XSetLineAttributes (display[iw], gc[iw], 0, LineDoubleDash, CapButt, JoinMiter); else { printf ("setLA(): cid = 0 illegal.\n"); exit(1); } lastLA[iw] = cid; } return; } /* end setLA() */ /* Clip line (xx[0],xx[1])-(xx[2],xx[3]) */ /* by rectangle (x0,y0)+(width,height). */ Bool box_clip_line (double x0,double y0,double width,double height,double xx[4]) { int i, j, m, n; double dir[2], c[6], cc, dd; Bool A,B; A = inside_box(width,height,xx[0]-x0,xx[1]-y0); B = inside_box(width,height,xx[2]-x0,xx[3]-y0); if (A&&B) return (True); dir[0] = xx[2]-xx[0]; dir[1] = xx[3]-xx[1]; /* distances of vertices to the line */ c[0] = (x0-xx[0])*(-dir[1])+(y0-xx[1])*dir[0]; c[1] = (x0+width-xx[0])*(-dir[1])+(y0-xx[1])*dir[0]; c[2] = (x0-xx[0])*(-dir[1])+(y0+height-xx[1])*dir[0]; c[3] = (x0+width-xx[0])*(-dir[1])+(y0+height-xx[1])*dir[0]; /* see if the segment lies within the window */ if ( (c[0]*c[1]>0) && (c[0]*c[2]>0) && (c[0]*c[3]>0) ) return (False); dd = sqrt(dir[0]*dir[0]+dir[1]*dir[1]); if (dd==0) return (False); dir[0] /= dd; dir[1] /= dd; if ( fabs(xx[2]-xx[0]) < W_TINY ) { c[2] = (y0+height-xx[1])*dir[1]; c[3] = (y0-xx[1])*dir[1]; m = 4; } else if ( fabs(xx[3]-xx[1]) < W_TINY ) { c[2] = (x0-xx[0])*dir[0]; c[3] = (x0+width-xx[0])*dir[0]; m = 4; } else { cc = 1+dir[1]*dir[1]/dir[0]/dir[0]; c[2] = (x0-xx[0])*dir[0]*cc; c[3] = (x0+width-xx[0])*dir[0]*cc; cc = 1+dir[0]*dir[0]/dir[1]/dir[1]; c[4] = (y0+height-xx[1])*dir[1]*cc; c[5] = (y0-xx[1])*dir[1]*cc; m = 6; } for (n=i=2; i<m; i++) if ((c[i]<dd+100*W_TINY)&&(c[i]>-100*W_TINY)&& inside_box(width,height,xx[0]+c[i]*dir[0]-x0, xx[1]+c[i]*dir[1]-y0)) c[n++] = c[i]; if (A) { xx[2] = xx[0] + c[2]*dir[0]; xx[3] = xx[1] + c[2]*dir[1]; } else if (B) { xx[0] += c[2]*dir[0]; xx[1] += c[2]*dir[1]; } else { if (n!=4) return(False); if (c[2]>c[3]) {cc=c[3];dd=c[2];} else {cc=c[2];dd=c[3];} xx[2] = xx[0] + dd*dir[0]; xx[3] = xx[1] + dd*dir[1]; xx[0] += cc*dir[0]; xx[1] += cc*dir[1]; } return (True); } /* end box_clip_line() */ /* Given that x[0,1,2] is inside the cubic box */ /* (x0,y0,z0)+(bx,by,bd), cut ray dx[0,1,2] by the box */ #define BOXCUT_MAXITER 32 double boxcut (double x0, double y0, double z0, double bx, double by, double bd, double x[3], double dx[3]) { Bool xm, ym, zm, xp, yp, zp; int i, n; double ch=1, cl=0, c=1; for (i=0; i<BOXCUT_MAXITER; i++) { /* binary search for a single plane-cut */ n = ((xm=x[0]+c*dx[0]<x0)?1:0) + ((xp=x[0]+c*dx[0]>x0+bx)?1:0) + ((ym=x[1]+c*dx[1]<y0)?1:0) + ((yp=x[1]+c*dx[1]>y0+by)?1:0) + ((zm=x[2]+c*dx[2]<z0)?1:0) + ((zp=x[2]+c*dx[2]>z0+bd)?1:0); if ( n>1 ) ch = c; else if ( n==0 ) { if (c==1) return (1.); else cl = c; } else break; c = (ch+cl)/2; } if (i==BOXCUT_MAXITER) return (c); else if (xm) return ((x0-x[0])/dx[0]); else if (xp) return ((x0+bx-x[0])/dx[0]); else if (ym) return ((y0-x[1])/dx[1]); else if (yp) return ((y0+by-x[1])/dx[1]); else if (zm) return ((z0-x[2])/dx[2]); else return ((z0+bd-x[2])/dx[2]); } /* end boxcut() */ /* see if it is necessary to draw the circle on screen */ Bool window_clip_circle(int iw,double x,double y,double r,Bool filled) { /* select 8 representative points on */ return( inside_box(win[iw].width,win[iw].height,x-r,y) || inside_box(win[iw].width,win[iw].height,x+r,y) || inside_box(win[iw].width,win[iw].height,x,y-r) || inside_box(win[iw].width,win[iw].height,x,y+r) || inside_box(win[iw].width,win[iw].height, x-r/sqrt(2.),y-r/sqrt(2.)) || inside_box(win[iw].width,win[iw].height, x+r/sqrt(2.),y-r/sqrt(2.)) || inside_box(win[iw].width,win[iw].height, x-r/sqrt(2.),y+r/sqrt(2.)) || inside_box(win[iw].width,win[iw].height, x+r/sqrt(2.),y+r/sqrt(2.)) || ( filled && ( inside_box(win[iw].width,win[iw].height,x,y) || inside_box(win[iw].width,win[iw].height,x-r/2,y) || inside_box(win[iw].width,win[iw].height,x+r/2,y) || inside_box(win[iw].width,win[iw].height,x,y-r/2) || inside_box(win[iw].width,win[iw].height,x,y+r/2) ) )); /* the circumference and 5 points inside */ } /* end window_clip_circle() */ /* convert reduced rw[] coordinates to s[] */ void rw2s (int iw, double rw[3], double s[3]) { double HI[3][3]; matinv ((double(*)[3])H, HI); s[0]=unit[iw]*(rw[0]*HI[0][0]+rw[1]*HI[1][0]+rw[2]*HI[2][0]); s[1]=unit[iw]*(rw[0]*HI[0][1]+rw[1]*HI[1][1]+rw[2]*HI[2][1]); s[2]=unit[iw]*(rw[0]*HI[0][2]+rw[1]*HI[1][2]+rw[2]*HI[2][2]); return; } /* end rw2s() */ /* convert s[] to reduced rw[] coordinates */ void s2rw (int iw, double s[3], double rw[3]) { rw[0] = (s[0]*H[0][0]+s[1]*H[1][0]+s[2]*H[2][0])/unit[iw]; rw[1] = (s[0]*H[0][1]+s[1]*H[1][1]+s[2]*H[2][1])/unit[iw]; rw[2] = (s[0]*H[0][2]+s[1]*H[1][2]+s[2]*H[2][2])/unit[iw]; return; } /* end s2rw() */ void s_truncate (double ds[3]) { while (ds[0]<-0.5) ds[0]++; while (ds[0]>0.5) ds[0]--; while (ds[1]<-0.5) ds[1]++; while (ds[1]>0.5) ds[1]--; while (ds[2]<-0.5) ds[2]++; while (ds[2]>0.5) ds[2]--; return; } /* end s_truncate() */ /* convert viewport axes readings to rw frame */ void va2rw (int iw, double va[3], double rw[3]) { rw[0] = va[0]*A[iw][0][0]+va[1]*A[iw][1][0]+va[2]*A[iw][2][0]; rw[1] = va[0]*A[iw][0][1]+va[1]*A[iw][1][1]+va[2]*A[iw][2][1]; rw[2] = va[0]*A[iw][0][2]+va[1]*A[iw][1][2]+va[2]*A[iw][2][2]; return; } /* end va2rw() */ /* convert rw frame to viewport axes readings */ void rw2va (int iw, double rw[3], double va[3]) { va[0] = dot(&A[iw][0][0], rw); va[1] = dot(&A[iw][1][0], rw); va[2] = dot(&A[iw][2][0], rw); return; } /* end rw2va() */ /* Given s[], calculate viewport axes reading va[0,1,2], */ /* distance to eye va[3], tilt and shrink factor va[4,5], */ /* and screen coordination x,y; returns visible or not. */ Bool s2screen (int iw, double s[3], Bool pbc) { double rx[3], zz; /* ws[] is a constant if PBC; otherwise it should */ /* be recalculated for every new animation frame. */ dws[iw][0] = s[0] - ws[iw][0]; dws[iw][1] = s[1] - ws[iw][1]; dws[iw][2] = s[2] - ws[iw][2]; /* only render stuff that is H-cubed around ws[] */ if (pbc) s_truncate (&dws[iw][0]); s2rw (iw, &dws[iw][0], rx); /* readings in viewport axes by inner products */ rw2va (iw, rx, &va[iw][0]); /* z-distance to the eye: if it passes the eye, */ /* then avoid singular transitions. */ zz = fabs(eye[iw]-va[iw][2]); if (zz<W_TINY) zz=W_TINY; /* distance to the eye */ va[iw][3] = sqrt(zz*zz+va[iw][0]*va[iw][0]+va[iw][1]*va[iw][1]); /* tilt factor: consider eye-sphere cone cut by */ /* the viewport plane: long axis>1, short axis=1 */ va[iw][4] = va[iw][3] / zz; /* sqrt(area) shrink factor */ va[iw][5] = eye[iw] / zz * sqrt(va[iw][4]) ; x[iw] = win[iw].width/2. + va[iw][0]*eye[iw]/zz*W_RESOLUTION; y[iw] = win[iw].height/2. - va[iw][1]*eye[iw]/zz*W_RESOLUTION; /* I don't see stuff plate_thickness[iw] in front of the */ /* plate, nor eye[iw]*eye_thickness_factor[iw] behind it */ front_clipped[iw] = (va[iw][2]<-plate_thickness[iw]); behind_clipped[iw] = (va[iw][2]>eye[iw]*eye_thickness_factor[iw]); /* window + viewplate front + behind clipping */ return( inside_box(win[iw].width, win[iw].height, x[iw], y[iw]) && (!front_clipped[iw]) && (!behind_clipped[iw]) ); } /* end s2screen() */ /* calculate the cid of a faded color (in pitch-black background) */ int fade_cid (int orig_cid, double intensity) { int CI = abs(orig_cid) - 1; if ( (CI<0) || (CI>=W_MAXCOLOR) ) { printf ("fade_cid(): invalid orig_cid = %d.\n", orig_cid); exit(1); } if (intensity >= 1) return (orig_cid); else if (intensity <= W_TINY) CI = W_BLACK-1; else CI += W_MAXCOLOR * floor( W_COLOR_RESOLUTION * (1-intensity) ); return (copysign(CI+1,orig_cid)); } /* end fade_cid() */ /* Draw a line on the window with automatic window clipping */ void drawline (int iw, int cid, double x[4]) { double xx[4]; xx[0] = x[0]; xx[1] = x[1]; xx[2] = x[2]; xx[3] = x[3]; if (box_clip_line(0,0,win[iw].width,win[iw].height,xx)) { setlcid(iw, cid); XDrawLine (display[iw], pixmap[iw], gc[iw], rint(xx[0]), rint(xx[1]), rint(xx[2]), rint(xx[3])); if ( drawps[iw] ) { convert_to_ps_color(iw); if ( cid > 0 ) PDrawLine (xx[0], xx[1], xx[2], xx[3]); else if ( cid < 0 ) PDrawDashLine (xx[0], xx[1], xx[2], xx[3]); } } return; } /* end drawline() */ /* Note: in buffered draw, if abs(cid)<=MAXCOLOR, then */ /* the light intensity is decided in the end (after */ /* minimum d is computed). Otherwise if a faded cid is */ /* already supplied, it will be used in the end as it is. */ /* draw a line on the graphic device, finally ordered by d */ void buffered_drawline (int iw, int cid, double x[4], double d) { int i = buffer_top[iw]++; if (i >= W_MAX_BUFFER) { printf ("W: buffer (line) overflow.\n"); exit(1); } buffer[iw][DBLE_BUFFER*i] = W_LINE; /* graph type */ buffer[iw][DBLE_BUFFER*i+1] = cid; /* records.. */ buffer[iw][DBLE_BUFFER*i+2] = x[0]; buffer[iw][DBLE_BUFFER*i+3] = x[1]; buffer[iw][DBLE_BUFFER*i+4] = x[2]; buffer[iw][DBLE_BUFFER*i+5] = x[3]; buffer_d[iw][i] = d; /* global ordering token */ return; } /* end buffered_drawline() */ void buffered_drawsegs (int iw, int cid, double x[4], double d1, double d2) { int i, j, k; double ds, df, tot, xx[4]; const double ratio = 1+1./W_COLOR_GRADE; if ( (order[iw] != W_ORDER_BY_Z) || /* only then is d linear */ (abs(cid) > W_MAXCOLOR) || (cid==0) || ( (fabs(d1) > W_TINY) && ( d2 / d1 < ratio ) && /* small variation */ ( d2 / d1 > 1 / ratio )) || ( (tot=norm(x[2]-x[0],x[3]-x[1])) <= /* too short */ W_MIN_SEG_LENGTH*W_MIN_SEG_LENGTH) ) buffered_drawline (iw, cid, x, d_line(d1,d2)); else { tot = sqrt(tot); for (k=W_COLOR_GRADE; tot/k<W_MIN_SEG_LENGTH; k--); for (i=0; i<k; i++) { ds = d1 + (d2-d1)/k*i; df = d1 + (d2-d1)/k*(i+1); xx[0] = x[0] + (ds-d1)*(x[2]-x[0])/(d2-d1); xx[1] = x[1] + (ds-d1)*(x[3]-x[1])/(d2-d1); xx[2] = x[0] + (df-d1)*(x[2]-x[0])/(d2-d1); xx[3] = x[1] + (df-d1)*(x[3]-x[1])/(d2-d1); buffered_drawline (iw, cid, xx, d_line(ds,df)); } } return; } /* end buffered_drawsegs() */ /* quick and dirty implementation */ void buffered_drawrectangle (int iw, double x0, double y0, double width, double height, int cid, double d) { double x[4]; x[0]=x0; x[1]=y0; x[2]=x0+width; x[3]=y0; buffered_drawline (iw, cid, x, d); x[0]=x[2]; x[1]=x[3]; x[2]=x0+width; x[3]=y0+height; buffered_drawline (iw, cid, x, d); x[0]=x[2]; x[1]=x[3]; x[2]=x0; x[3]=y0+height; buffered_drawline (iw, cid, x, d); x[0]=x[2]; x[1]=x[3]; x[2]=x0; x[3]=y0; buffered_drawline (iw, cid, x, d); return; } /* end buffered_drawrectangle() */ /* draw a circle on the window with automatic window clipping */ void drawcircle (int iw, double x, double y, double a, int cid) { setfg (iw, cid); if (a<1) return; else if ((a==1)&&inside_box(win[iw].width,win[iw].height,x,y)) { XDrawPoint(display[iw], pixmap[iw], gc[iw], x, y); } else if ((a==2)&&window_clip_circle(iw,x,y,1.,cid>0)) { XDrawPoint(display[iw], pixmap[iw], gc[iw], x+1, y); XDrawPoint(display[iw], pixmap[iw], gc[iw], x-1, y); XDrawPoint(display[iw], pixmap[iw], gc[iw], x, y+1); XDrawPoint(display[iw], pixmap[iw], gc[iw], x, y-1); if (cid>0) XDrawPoint(display[iw], pixmap[iw], gc[iw], x, y); } else if ( window_clip_circle(iw,x,y,a,cid>0) ) { if (cid > 0) { /* filled -> circumference is solid line */ setLA (iw, 1); XFillArc(display[iw], pixmap[iw], gc[iw], x-a, y-a, a*2, a*2, 0, 360*64); } else { /* empty -> circumference is dash line */ setLA (iw, -1); XDrawArc(display[iw], pixmap[iw], gc[iw], x-a, y-a, a*2, a*2, 0, 360*64); } } if ( drawps[iw] ) { convert_to_ps_color (iw); if (cid > 0) PDrawCircle (x, y, a, P_FILL); else PDrawDashCircle (x, y, a); } return; } /* end drawcircle() */ /* buffered draw a circle */ void buffered_drawcircle (int iw, double x, double y, double a, int cid, double d) { int i = buffer_top[iw]++; if (i >= W_MAX_BUFFER) { printf ("W: buffer (circle) overflow.\n"); exit(1); } buffer[iw][DBLE_BUFFER*i] = W_SPHERE; /* graph type */ buffer[iw][DBLE_BUFFER*i+1] = x; /* records... */ buffer[iw][DBLE_BUFFER*i+2] = y; buffer[iw][DBLE_BUFFER*i+3] = a; buffer[iw][DBLE_BUFFER*i+4] = cid; buffer_d[iw][i] = d; /* global ordering token */ return; } /* end buffered_drawcircle() */ /* compute the light intensity according to beta and alpha, */ /* Z-buffer token d and its achieved minimum for real objects. */ double lighting (int iw, double d) { /* highlight everything that is within beta[iw] */ double d0 = min_d[iw]+beta[iw]; if (d < d0) return(1.); switch (order[iw]) { case W_ORDER_BY_R: return (exp((d0-d) * alpha[iw])); case W_ORDER_BY_Z: return (pow(1/(1+d-d0), alpha[iw])); default: printf ("lighting: invalid Z-buffer scheme %d.\n", order[iw]); exit(1); } return(0.); } /* end lighting() */ /* empty the graphical request queue, */ /* but reordering them according to d */ int flush_buffer (int iw) { int i, n = buffer_top[iw], idx[W_MAX_BUFFER]; /* claimed to be the fastest sorting scheme around */ quicksort (n, &buffer_d[iw][0], idx); /* largest d gets drawed first */ i = W_MAX_DRAW_BUFFER_OBJECTS - 1; if (i > n-1) i = n-1; for (; i>=0; i--) switch ((int)buffer[iw][DBLE_BUFFER*idx[i]]) { case W_SPHERE: drawcircle(iw, buffer[iw][DBLE_BUFFER*idx[i]+1], buffer[iw][DBLE_BUFFER*idx[i]+2], buffer[iw][DBLE_BUFFER*idx[i]+3], rcid(buffer[iw][DBLE_BUFFER*idx[i]+4], lighting(iw,buffer_d[iw][i]))); break; case W_LINE: drawline (iw, rcid(buffer[iw][DBLE_BUFFER*idx[i]+1], lighting(iw,buffer_d[iw][i])), &buffer[iw][DBLE_BUFFER*idx[i]+2]); break; default: printf ("object type %d does not exist.\n", buffer[iw][DBLE_BUFFER*idx[i]]); exit(1); } buffer_top[iw] = 0; return (n); } /* end flush_buffer() */ /* If PBC, render a physical bond between two points; */ /* otherwise draw a line on screen linking them. */ void Render_LINE (int iw, int n, WLINE gp[]) { int i; double sw[6], ds[3], s[3], c, ad, bd, xx[4], zz[4]; Bool av, bv, cv, af, bf, ab, bb; for (i=0; i<n; i++) if ( gp[i].cid!=0 ) { /* a and b are starting and ending points of the line */ av = s2screen (iw, &gp[i].s[0], PBC[iw]); /* visible */ af = front_clipped[iw]; /* clipped at front */ ab = behind_clipped[iw]; /* clipped at back */ xx[0] = x[iw]; xx[1] = y[iw]; sw[0] = dws[iw][0]; sw[1] = dws[iw][1]; sw[2] = dws[iw][2]; ad = d_token(iw); /* distance to the eye */ bv = s2screen (iw, &gp[i].s[3], PBC[iw]); bf = front_clipped[iw]; bb = behind_clipped[iw]; xx[2] = x[iw]; xx[3] = y[iw]; sw[3] = dws[iw][0]; sw[4] = dws[iw][1]; sw[5] = dws[iw][2]; bd = d_token(iw); if ( !PBC[iw] ) { /* direct draw */ if ( (!(af&&bf)) && (!(ab&&bb)) ) buffered_drawsegs (iw, gp[i].cid, xx, ad, bd); /* if both ends are clipped on one side, */ /* there is no hope in seeing their link */ } else { /* compute real bonding direction */ ds[0] = sw[3] - sw[0]; ds[1] = sw[4] - sw[1]; ds[2] = sw[5] - sw[2]; s_truncate (ds); c = boxcut (-0.5,-0.5,-0.5,1.,1.,1.,sw,ds); s[0] = ws[iw][0] + sw[0] + c*ds[0]; s[1] = ws[iw][1] + sw[1] + c*ds[1]; s[2] = ws[iw][2] + sw[2] + c*ds[2]; cv = s2screen (iw, s, False); if ( (!(af&&front_clipped[iw])) && (!(ab&&behind_clipped[iw])) ) { zz[0] = xx[0]; zz[1] = xx[1]; zz[2] = x[iw]; zz[3] = y[iw]; buffered_drawsegs (iw, gp[i].cid, zz, ad, d_token(iw)); /* indicate that the bond pierces through the PBC */ if ( fabs(c-1) > W_TINY ) buffered_drawcircle (iw, zz[2], zz[3], W_TOUCH_SIZE, W_TOUCH_CID, d_token(iw)); } /* first part of the bond */ if ( fabs(c-1) > W_TINY ) { ds[0] = -ds[0]; ds[1] = -ds[1]; ds[2] = -ds[2]; c = boxcut (-0.5,-0.5,-0.5,1.,1.,1.,sw+3,ds); s[0] = ws[iw][0] + sw[3] + c*ds[0]; s[1] = ws[iw][1] + sw[4] + c*ds[1]; s[2] = ws[iw][2] + sw[5] + c*ds[2]; cv = s2screen (iw, s, False); if ( (!(bf&&front_clipped[iw])) && (!(bb&&behind_clipped[iw])) ) { zz[0] = xx[2]; zz[1] = xx[3]; zz[2] = x[iw]; zz[3] = y[iw]; buffered_drawsegs (iw, gp[i].cid, zz, bd, d_token(iw)); buffered_drawcircle (iw, zz[2], zz[3], W_TOUCH_SIZE, W_TOUCH_CID, d_token(iw)); } } /* second part of the bond */ } /* PBC[iw] */ } /* loop through all lines */ return; } /* end Render_LINE() */ /* point is a volume-less beacon that appears with fixed size on screen */ void Render_POINT (int iw, int n, WPOINT gp[]) { int i; WLINE line[1]; for (i=0; i<n; i++) if (gp[i].lcid != 0) { line[0].s[0] = gp[i].s[0]; line[0].s[1] = gp[i].s[1]; line[0].s[2] = gp[i].s[2]; line[0].s[3] = gp[(i+1)%n].s[0]; /* the "next" point */ line[0].s[4] = gp[(i+1)%n].s[1]; line[0].s[5] = gp[(i+1)%n].s[2]; line[0].cid = gp[i].lcid; Render_LINE (iw, 1, line); } for (i=0; i<n; i++) { /* cid=0 provides a convenient method to */ /* hide an object without deleting it. */ if (gp[i].cid==0) continue; s2screen (iw, gp[i].s, PBC[iw]); if ((!front_clipped[iw]) && (!behind_clipped[iw]) && window_clip_circle(iw,x[iw],y[iw], gp[i].size,gp[i].cid>0)) buffered_drawcircle (iw, x[iw], y[iw], gp[i].size, gp[i].cid, d_token(iw)); } return; } /* end Render_POINT() */ /* Since Xlib cannot draw tilted ellipse, I can */ /* only use a circle with the same projected area */ void Render_SPHERE (int iw, int n, WSPHERE gp[]) { int i, cid; double r, a, rt; /* t is for touch */ for (i=0; i<n; i++) { if ( gp[i].cid == 0 ) continue; s2screen (iw, gp[i].s, PBC[iw]); r = (double) gp[i].radius / unit[iw]; cid = gp[i].cid; rt=0; if ( ( va[iw][2] < -plate_thickness[iw] - r ) || ( va[iw][2] > eye[iw]*eye_thickness_factor[iw] + r ) ) continue; /* clipped by front or back */ else if ( va[iw][2] < -plate_thickness[iw] + r ) { rt = sqrt( r*r-(va[iw][2]+plate_thickness[iw])* (va[iw][2]+plate_thickness[iw]) ); if (va[iw][2] < -plate_thickness[iw]) r = rt; } else if ( va[iw][2] > eye[iw]*eye_thickness_factor[iw] - r ) { rt = sqrt(r*r-(va[iw][2]-eye[iw]*eye_thickness_factor[iw]) *(va[iw][2]-eye[iw]*eye_thickness_factor[iw])); if (va[iw][2] > eye[iw]*eye_thickness_factor[iw]) r = rt; } a = r * va[iw][5] * W_RESOLUTION; if ( window_clip_circle(iw,x[iw],y[iw],a,cid>0) ) buffered_drawcircle (iw, x[iw], y[iw], a, cid, d_token(iw)); if ( rt==0 ) continue; cid = -W_BLACK; a = rt * va[iw][5] * W_RESOLUTION - 1; if ( window_clip_circle(iw,x[iw],y[iw],a,cid>0) ) buffered_drawcircle (iw, x[iw], y[iw], a, cid, d_token(iw)-W_TINY); } return; } /* end Render_SPHERE() */ /* event handler, happens before rendering */ Bool translate_window (int iw, int i, double delta) { if (PBC[iw]) s2rw(iw, &ws[iw][0], &w[iw][0]); w[iw][0] += delta * A[iw][i][0]; w[iw][1] += delta * A[iw][i][1]; w[iw][2] += delta * A[iw][i][2]; if (PBC[iw]) rw2s(iw, &w[iw][0], &ws[iw][0]); return (True); } /* end translate_window() */ /* If "big-circle" rotation is a->b, what happens to W? */ /* assume a and b are already normalized and NOT collinear */ void rotate (double a[3], double b[3], double W[3]) { double c[4], ca[3], cb[3], wa, wc, wca; cross (a, b, c); c[3] = sqrt( dot(c,c) ); c[0] /= c[3]; c[1] /= c[3]; c[2] /= c[3]; cross (c, a, ca); cross (c, b, cb); wc = dot (W, c); wa = dot (W, a); wca = dot (W, ca); /* recombine in the rotated coordinate frame */ W[0] = wc*c[0] + wa*b[0] + wca*cb[0]; W[1] = wc*c[1] + wa*b[1] + wca*cb[1]; W[2] = wc*c[2] + wa*b[2] + wca*cb[2]; return; } /* end rotate() */ void rotate_window_origin (int iw, double start[3], double finish[3], int mpivot) { double pivot_rw[3], wr[3]; if (PBC[iw]) s2rw(iw, &ws[iw][0], &w[iw][0]); switch ( mpivot ) { case W_EYE_PIVOT: assign (&w[iw][0], pivot_rw); pivot_rw[0] += A[iw][2][0] * eye[iw]; pivot_rw[1] += A[iw][2][1] * eye[iw]; pivot_rw[2] += A[iw][2][2] * eye[iw]; break; case W_MID_PIVOT: assign (&w[iw][0], pivot_rw); pivot_rw[0] += A[iw][2][0] * eye[iw] / 2.; pivot_rw[1] += A[iw][2][1] * eye[iw] / 2.; pivot_rw[2] += A[iw][2][2] * eye[iw] / 2.; break; case W_VIEWPLATE_PIVOT: return; case W_OBJECTS_PIVOT: /* this is not local navigation */ if ( !PBC[iw] ) { /* rotation pivot is H center of mass */ pivot_rw[0] = (H[0][0]+H[1][0]+H[2][0])/unit[iw]/2.; pivot_rw[1] = (H[0][1]+H[1][1]+H[2][1])/unit[iw]/2.; pivot_rw[2] = (H[0][2]+H[1][2]+H[2][2])/unit[iw]/2.; break; } else return; default: printf ("rotate_window_origin: invalid pivot %d.\n", pivot); exit(1); } w[iw][0] -= pivot_rw[0]; w[iw][1] -= pivot_rw[1]; w[iw][2] -= pivot_rw[2]; rotate (start, finish, &w[iw][0]); w[iw][0] += pivot_rw[0]; w[iw][1] += pivot_rw[1]; w[iw][2] += pivot_rw[2]; if (PBC[iw]) rw2s (iw, &w[iw][0], &ws[iw][0]); return; } /* end rotate_window_origin() */ void rotate_window (int iw, double start[3], double finish[3], int mpivot) { /* viewplate origin first */ rotate_window_origin (iw, start, finish, mpivot); /* rotate axes */ rotate (start, finish, &A[iw][0][0]); normalize (&A[iw][0][0]); rotate (start, finish, &A[iw][1][0]); normalize (&A[iw][1][0]); /* numerically constrain the orthogonality */ cross (&A[iw][0][0], &A[iw][1][0], &A[iw][2][0]); cross (&A[iw][1][0], &A[iw][2][0], &A[iw][0][0]); return; } /* end rotate_window() */ /* magic sphere: map a plane onto an upper half-sphere surface */ void MGS (double x, double y, double r, double a[3]) { double a2; if ( (a2=x*x+y*y) < r*r ) { a[0] = x / r; a[1] = y / r; a[2] = sqrt(1.-a[0]*a[0]-a[1]*a[1]); } else { a[0] = x / sqrt(a2); a[1] = y / sqrt(a2); a[2] = 0.; } return; } /* end MGS() */ #define MIN_RADIAN (2*W_PI/180) Bool mouse_rotate_window (int iw, int to_x, int to_y, int mpivot) { double a[3], b[3], c[3], ra[3], rb[3]; /* pointer starting position */ MGS ( lx[iw] - win[iw].width/2., -ly[iw] + win[iw].height/2., mgs_size[iw], a ); /* pointer ending position */ MGS ( to_x - win[iw].width/2., -to_y + win[iw].height/2., mgs_size[iw], b ); cross (a, b, c); if ( dot(c,c) < MIN_RADIAN*MIN_RADIAN ) return (False); va2rw (iw, a, ra); va2rw (iw, b, rb); rotate_window (iw, rb, ra, mpivot); /* save pointer motion */ lx[iw] = to_x; ly[iw] = to_y; return (True); } /* end mouse_rotate_window() */ /* rotate viewport axes by theta around rw axis rc[] */ void axis_rotate_window (int iw, double rc[3], double theta, int mpivot) { double ra[3], rb[3], rar[3]; ra[0] = frandom() - 0.5; ra[1] = frandom() - 0.5; ra[2] = frandom() - 0.5; cross (rc, ra, rb); normalize (rb); cross (rb, rc, ra); normalize (ra); rar[0] = cos(theta)*ra[0] + sin(theta)*rb[0]; rar[1] = cos(theta)*ra[1] + sin(theta)*rb[1]; rar[2] = cos(theta)*ra[2] + sin(theta)*rb[2]; rotate_window (iw, rar, ra, mpivot); return; } /* end axis_rotate_window() */ /* dump the screen in ASCII xpm format */ int capture_xpm (int iw) { char filename[2*W_MAX_STRLEN], *p, *q; sprintf (filename, "%s_%d.xpm", &title[iw][0], xpm_counter[iw]); /* fill white space by underline */ for (q=filename; (*q!=(char)0); q++) if ((*q==' ')||(*q=='\t')) *q = '_'; printf("\nSave screen %d on (default=\"%s\"): ", iw, filename); for (p=q; p<filename+2*W_MAX_STRLEN-5; p++) if ((*p=(char)getc(stdin))=='\n') break; if (p==q) q=filename; *p = (char)0; /* add file suffix if without */ if (strcasecmp(p-4,".xpm")) sprintf(p,".xpm"); else p=p-4; if ( freetowrite(q) ) { XpmWriteFileFromPixmap (display[iw], q, pixmap[iw], NULL, NULL); printf ("saved on \"%s\".\n", q); xpm_counter[iw]++; } printf ("\n"); return (xpm_counter[iw]); } /* end capture_xpm() */ /* dump the screen in Encapsulated Postscript format */ int init_ps (int iw) { char filename[2*W_MAX_STRLEN], *p, *q; FILE *P; sprintf (filename, "%s_%d.eps", &title[iw][0], ps_counter[iw]); /* fill white space by underline */ for (q=filename; (*q!=(char)0); q++) if ((*q==' ')||(*q=='\t')) *q = '_'; printf("\nSave screen %d on (default=\"%s\"): ", iw, filename); for (p=q; p<filename+2*W_MAX_STRLEN-5; p++) if ((*p=(char)getc(stdin))=='\n') break; if (p==q) q=filename; *p = (char)0; /* add file suffix if without */ if (strcasecmp(p-4,".eps")) sprintf(p,".eps"); else p=p-4; /* make the PS device compatible with current X Window */ if ( freetowrite(q) ) { P = PInit(q, (double)win[iw].width/W_PS_INCH_RESOLUTION, 0., 0., (double)-win[iw].height/W_PS_INCH_RESOLUTION, P_DEFAULT, P_DEFAULT, win[iw].width, win[iw].height ); ps_counter[iw]++; } else printf ("\n"); drawps[iw] = (P!=NULL); return (ps_counter[iw]); } /* end init_ps() */ /* convert X Window RGB to Postscript RGB */ void convert_to_ps_color (int iw) { int CI = lastCI[iw]; double x = floor(CI/W_MAXCOLOR)/W_COLOR_RESOLUTION; x *= W_PS_COLOR_AMPLIFICATION; CI = CI % W_MAXCOLOR; if (CI==W_WHITE-1) PSetRGBColor (x,x,x); else if (CI==W_BLACK-1) PSetRGBColor (1-x,1-x,1-x); else /* mix color with white -- which is the background */ PSetRGBColor((1-x)*color[iw][CI].red /65535.+x, (1-x)*color[iw][CI].green/65535.+x, (1-x)*color[iw][CI].blue /65535.+x); return; } /* end convert_to_ps_color() */ void close_ps (int iw) { printf ("screenshot saved on %s.\n\n", PClose()); drawps[iw] = False; return; } /* end close_ps() */ void print_status (int iw) { printf("================= Status of window #%d =================\n",iw); printf("One render world unit = %f A, in rw frame:\n\n", unit[iw]); printf(" | %.3f %.3f %.3f | | %.3f %.3f %.3f |\n", H[0][0]/unit[iw], H[0][1]/unit[iw], H[0][2]/unit[iw], H[0][0], H[0][1], H[0][2]); printf("H = | %.3f %.3f %.3f | = | %.3f %.3f %.3f | A.\n", H[1][0]/unit[iw], H[1][1]/unit[iw], H[1][2]/unit[iw], H[1][0], H[1][1], H[1][2]); printf(" | %.3f %.3f %.3f | | %.3f %.3f %.3f |\n\n", H[2][0]/unit[iw], H[2][1]/unit[iw], H[2][2]/unit[iw], H[2][0], H[2][1], H[2][2]); if (PBC[iw]) s2rw (iw, &ws[iw][0], &w[iw][0]); else rw2s (iw, &w[iw][0], &ws[iw][0]); printf("Viewport center position ws = [%.4f %.4f %.4f],\n", ws[iw][0], ws[iw][1], ws[iw][2]); printf("w = [%.4f %.4f %.4f] = [%.3f %.3f %.3f] A.\n\n", w[iw][0], w[iw][1], w[iw][2], w[iw][0]*unit[iw], w[iw][1]*unit[iw], w[iw][2]*unit[iw]); printf("vx = [ %.4f %.4f %.4f ] * %.4f = %.3f A,\n", A[iw][0][0], A[iw][0][1], A[iw][0][2], (double)win[iw].width/W_RESOLUTION, (double)win[iw].width/W_RESOLUTION*unit[iw]); printf("vy = [ %.4f %.4f %.4f ] * %.4f = %.3f A,\n", A[iw][1][0], A[iw][1][1], A[iw][1][2], (double)win[iw].height/W_RESOLUTION, (double)win[iw].width/W_RESOLUTION*unit[iw]); printf("vz = [ %.4f %.4f %.4f ] * %.4f = %.3f A.\n\n", A[iw][2][0], A[iw][2][1], A[iw][2][2], eye[iw], eye[iw]*unit[iw]); printf("Eye thickness factor = %.4f -> %.3f A (behind clip),\n", eye_thickness_factor[iw], eye[iw]*eye_thickness_factor[iw]*unit[iw] ); printf("viewplate thickness = %.4f = %.3f A (front clip).\n\n", plate_thickness[iw], plate_thickness[iw]*unit[iw] ); printf("PBC = %s, factor of change = %.3f.\n\n", PBC[iw]?"TRUE":"FALSE", factor[iw]); printf("Z-buffer scheme is \"%s\",\n", W_ORDER_NAME(order[iw])); printf("lighting alpha = %.2f, beta = %.2f.\n\n", alpha[iw], beta[iw]); printf("Rotation pivot type is \"%s\".\n\n", W_PIVOT_NAME(pivot[iw])); printf("Window size = [%d %d] pixels, color depth = %d\n", win[iw].width, win[iw].height, color_depth[iw]); printf("pointer XY: this = [%d %d] last = [%d %d].\n\n", px[iw], py[iw], lx[iw], ly[iw]); printf("This animator has lived for approx. %.1f seconds,\n", timespent[iw]); printf ("frames rendered = %d, designed refresh rate = %.1f,\n", nanim[iw], rate[iw]); printf("average refresh rate till now = %.1f frames/s.\n", nanim[iw]/timespent[iw]); printf("========================================================\n"); return; } /* print_status() */ /* draw a tunnel, plate ends, MGS and a cross to guide the eye */ void draw_eye_aids (int iw) { int ix, iy; double cx, cy, cl=10, sf_eye, sf_plate, ad, bd, s, x[4], r=W_GOLDEN_RATIO; cx = win[iw].width / 2.; cy = win[iw].height / 2.; sf_eye = (eye_thickness_factor[iw] > 1-r)? 1./r+W_TINY: 1./(1-eye_thickness_factor[iw]); sf_plate = eye[iw] / (eye[iw]+plate_thickness[iw]); if (sf_eye > sf_plate) { /* draw the tunnel as long as the slice > 0 */ ad = eye[iw] - eye_thickness_factor[iw]*eye[iw]; bd = eye[iw] + plate_thickness[iw]; for (ix=-1; ix<=1; ix+=2) for (iy=-1; iy<=1; iy+=2) { x[0] = cx*(1+ix*r*sf_eye); x[1] = cy*(1+iy*r*sf_eye); x[2] = cx*(1+ix*r*sf_plate); x[3] = cy*(1+iy*r*sf_plate); buffered_drawsegs (iw, W_TUNNEL_CID, x, ad, bd); } /* eye end */ if (sf_eye*r < 1) buffered_drawrectangle (iw, cx*(1-r*sf_eye), cy*(1-r*sf_eye), 2*cx*r*sf_eye, 2*cy*r*sf_eye, W_TUNNEL_CID, ad); /* plate end */ buffered_drawrectangle (iw, cx*(1-r*sf_plate), cy*(1-r*sf_plate), 2*cx*r*sf_plate, 2*cy*r*sf_plate, W_TUNNEL_CID, bd); } /* magic sphere base is at z = 0 */ buffered_drawcircle (iw, cx, cy, mgs_size[iw], W_MGS_CID, eye[iw]); /* draw a cross at center with height mgs_size */ x[0] = cx-cl; x[1] = cy; x[2] = cx+cl; x[3] = cy; buffered_drawline (iw, W_CROSS_CID, x, eye[iw]-(double)mgs_size[iw]/W_RESOLUTION); x[0] = cx; x[1] = cy-cl; x[2] = cx; x[3] = cy+cl; buffered_drawline (iw, W_CROSS_CID, x, eye[iw]-(double)mgs_size[iw]/W_RESOLUTION); return; } /* end draw_eye_aids() */ void print_help (int iw) { printf("*****************************************************\n"); printf("List of actions (window #%d):\n\n",iw); printf("F1: Help\n"); printf("F2: Print out status of this window\n"); printf("F3: Clone this window\n"); printf("F4: add a Default window\n"); printf("F5: Restore collinear axes\n"); printf("F6: save screenshot to .Xpm file\n"); printf("F7: save screenshot to .epS file\n"); printf("F8: toggle eye Aid\n"); printf("F9: toggle pBc\n"); printf("F10: toggle Z-buffer scheme\n"); printf("Ee: Eye as pivot\n"); printf("Mm: eye-plate Midpoint as pivot\n"); printf("Vv: Viewplate origin as pivot\n"); printf("Oo: center of Objects as pivot\n"); printf("ESC: Kill this window\n"); printf("q: close all windows\n\n"); printf("0 to 9: toggle rate of change\n"); printf("<1>0.0005 <2>0.001 <3>0.002 <4>0.005 <5>0.01\n"); printf("<6>0.02 <7>0.05 <8>0.1 <9>0.25 <0>%.3f\n\n", W_DEFAULT_FACTOR); printf("Use mouse to change orientation of the viewport\n\n"); printf("Up/Down/Right/Left/Shift+Up/Down: viewport shift\n"); printf("Ctrl+Alt+(etc.): viewport rotation around axes\n"); printf("Ctrl+Shift+Up/Down: de/increase eye locus\n"); printf("Ctrl+Up/Down: de/increase eye thickness factor\n"); printf("Ctrl+Right/Left: in/decrease viewplate thickness\n"); printf("Home/End: de/increase lighting exponent alpha\n"); printf("Shift+Home/End: in/decrease lighting margin beta\n"); printf("Ctrl+Shift+Home/End: restore alpha/beta defaults\n"); printf("PageUp/Down: de/increase length-scale of observer\n"); printf("****************************************************\n"); return; } /* end print_help() */ /* manager of key/mouse events for the Animator */ Bool treatevent (int iw, XEvent *e) { int a, b; Bool c; XSizeHints newhint; Window root_return, child_return; c = XQueryPointer (display[iw], windows[iw], &root_return, &child_return, &a, &b, &px[iw], &py[iw], &mask[iw]); switch (e->type) { case ButtonPress: if ( e->xbutton.time - last_button_press_time[iw] < W_DEFAULT_DBLE_CLICK_IN_MS ) { /* double click event */ /* shift the pointer to the center */ XWarpPointer (display[iw], windows[iw], None, 0, 0, 0, 0, -px[iw]+win[iw].width/2., -py[iw]+win[iw].height/2.); last_button_press_time[iw] = -W_HUGE; lx[iw] = win[iw].width / 2.; ly[iw] = win[iw].height / 2.; return( mouse_rotate_window (iw, px[iw], py[iw], pivot[iw]) ); } else { lx[iw] = px[iw]; ly[iw] = py[iw]; last_button_press_time[iw] = e->xbutton.time; return (False); } case ButtonRelease: case MotionNotify: return( mouse_rotate_window (iw, px[iw], py[iw], pivot[iw]) ); case Expose: XGetGeometry (display[iw], windows[iw], &root[iw], &newhint.x, &newhint.y, (unsigned *) &newhint.width, (unsigned *) &newhint.height, &border_width[iw], &color_depth[iw] ); if ( (newhint.width-1 != win[iw].width) || (newhint.height-1 != win[iw].height) ) { /* the window has been resized */ /* old pixmap cannot be used */ XFreeGC (display[iw], gc[iw]); XFreePixmap (display[iw], pixmap[iw]); win[iw].width = newhint.width - 1; win[iw].height = newhint.height - 1; pixmap[iw] = XCreatePixmap ( display[iw], root[iw], win[iw].width, win[iw].height, color_depth[iw] ); /* magic sphere has the size of the tunnel */ mgs_size[iw] = W_GOLDEN_RATIO * sqrt((double)win[iw].width*win[iw].height/W_PI); gc[iw] = XCreateGC(display[iw],pixmap[iw],0,0); } return (True); case KeyPress: #define CONTROL (((XKeyEvent*)e)->state & ControlMask) #define SHIFT (((XKeyEvent*)e)->state & ShiftMask) #define META (((XKeyEvent*)e)->state & Mod1Mask) /* if the pointer is not in this window, ignore */ if (!c) return (False); switch (XKeycodeToKeysym (display[iw], e->xkey.keycode, 0)) { case XK_F1: case XK_h: print_help(iw); return (False); case XK_F2: case XK_p: print_status(iw); return (False); case XK_F3: case XK_c: /* clone this window */ if (PBC[iw]) s2rw (iw, &ws[iw][0], &w[iw][0]); WAddWindow (display_addr[iw], "W_DEFAULT", H, unit[iw], PBC[iw], w[iw][0], w[iw][1], w[iw][2], A[iw][0][0], A[iw][0][1], A[iw][0][2], A[iw][1][0], A[iw][1][1], A[iw][1][2], (double)win[iw].width /W_RESOLUTION, (double)win[iw].height/W_RESOLUTION, eye[iw], eye_thickness_factor[iw], plate_thickness[iw], order[iw], alpha[iw], beta[iw], factor[iw], eye_aid[iw], rate[iw]); return (False); case XK_F4: case XK_d: WAddDefaultWindow((double (*)[3])H); return (False); case XK_F5: case XK_r: A[iw][0][0]=1; A[iw][0][1]=0; A[iw][0][2]=0; A[iw][1][0]=0; A[iw][1][1]=1; A[iw][1][2]=0; A[iw][2][0]=0; A[iw][2][1]=0; A[iw][2][2]=1; return (True); case XK_F6: case XK_x: capture_xpm(iw); return (False); case XK_F7: case XK_s: init_ps(iw); return(drawps[iw]); case XK_F8: case XK_a: eye_aid[iw] = (eye_aid[iw]+1)%3; return (True); case XK_F9: case XK_b: PBC[iw] = !PBC[iw]; return (True); case XK_F10: case XK_z: order[iw] = (order[iw]+1)%2; return (True); case XK_e: pivot[iw]=W_EYE_PIVOT; return (False); case XK_m: pivot[iw]=W_MID_PIVOT; return (False); case XK_v: pivot[iw]=W_VIEWPLATE_PIVOT; return (False); case XK_o: pivot[iw]=W_OBJECTS_PIVOT; return (False); case XK_Escape: case XK_k: WCloseWindow(iw); return (False); case XK_q: WCloseAllWindows(); return (False); case XK_Right: if (META&&CONTROL) /* rotate */ axis_rotate_window (iw, &A[iw][0][0], 2*W_PI*factor[iw], pivot[iw]); else if (CONTROL) /* change viewplate thickness */ plate_thickness[iw] *= (1+factor[iw]); else translate_window (iw, 0, factor[iw]); /* shift */ return (True); case XK_Left: if (META&&CONTROL) axis_rotate_window (iw, &A[iw][0][0], -2*W_PI*factor[iw], pivot[iw]); else if (CONTROL) plate_thickness[iw] /= (1+factor[iw]); else translate_window (iw, 0, -factor[iw]); return (True); case XK_Up: if (META&&CONTROL) if (SHIFT) axis_rotate_window (iw, &A[iw][2][0], -2*W_PI*factor[iw], pivot[iw]); else axis_rotate_window (iw, &A[iw][1][0], 2*W_PI*factor[iw], pivot[iw]); else if (CONTROL) if (SHIFT) eye[iw] /= (1+factor[iw]); else eye_thickness_factor[iw] -= factor[iw]; else if (SHIFT) /* -z direction */ translate_window (iw, 2, -factor[iw]); else translate_window (iw, 1, factor[iw]); return(True); case XK_Down: if (META&&CONTROL) if (SHIFT) axis_rotate_window (iw, &A[iw][2][0], 2*W_PI*factor[iw], pivot[iw]); else axis_rotate_window (iw, &A[iw][1][0], -2*W_PI*factor[iw], pivot[iw]); else if (CONTROL) if (SHIFT) eye[iw] *= (1+factor[iw]); else { eye_thickness_factor[iw] += factor[iw]; if (eye_thickness_factor[iw] > 1-W_TINY) { printf ("eye_thickness_factor > 1, stopped.\n"); eye_thickness_factor[iw] = 1-W_TINY; } } else if (SHIFT) translate_window (iw, 2, factor[iw]); else translate_window (iw, 1, -factor[iw]); return (True); case XK_Page_Up: unit[iw] /= (1+factor[iw]); if ( !PBC[iw] ) { w[iw][0] *= (1+factor[iw]); w[iw][1] *= (1+factor[iw]); w[iw][2] *= (1+factor[iw]); } return (True); case XK_Page_Down: unit[iw] *= (1+factor[iw]); if ( !PBC[iw] ) { w[iw][0] /= (1+factor[iw]); w[iw][1] /= (1+factor[iw]); w[iw][2] /= (1+factor[iw]); } return (True); case XK_Home: if (CONTROL&&SHIFT) alpha[iw] = W_DEFAULT_ALPHA; else if (SHIFT) beta[iw] += factor[iw]; else alpha[iw] /= (1+factor[iw]); return (True); case XK_End: if (CONTROL&&SHIFT) beta[iw] = W_DEFAULT_BETA; else if (SHIFT) beta[iw] -= factor[iw]; else alpha[iw] *= (1+factor[iw]); return(True); case XK_1: factor[iw]=0.0005; return(False); case XK_2: factor[iw]=0.001; return(False); case XK_3: factor[iw]=0.002; return(False); case XK_4: factor[iw]=0.005; return(False); case XK_5: factor[iw]=0.01; return(False); case XK_6: factor[iw]=0.02; return(False); case XK_7: factor[iw]=0.05; return(False); case XK_8: factor[iw]=0.1; return(False); case XK_9: factor[iw]=0.25; return(False); case XK_0: factor[iw] = W_DEFAULT_FACTOR; return(False); default: return (False); } default: return (False); } } /* end treatevent() */ /* Draw on created window, then sleep for a while; */ /* handle events first if the queue is not empty. */ void Animate (int iw) { int i, j; struct OGL *p; Bool pbc, nontrivial_event; XEvent event; struct timeval tp; double inittime, rendertime; WPOINT box[4][4], pbox[4][4]; double ds[8][3] = { {-0.5, -0.5, -0.5}, /* 0 */ { 0.5, -0.5, -0.5}, /* 1 */ { 0.5, 0.5, -0.5}, /* 2 */ {-0.5, 0.5, -0.5}, /* 3 */ {-0.5, -0.5, 0.5}, /* 4 */ { 0.5, -0.5, 0.5}, /* 5 */ { 0.5, 0.5, 0.5}, /* 6 */ {-0.5, 0.5, 0.5} /* 7 */ }; /* 0123 face */ assign ( &ds[0][0], &(box[0][0].s[0]) ); assign ( &ds[1][0], &(box[0][1].s[0]) ); assign ( &ds[2][0], &(box[0][2].s[0]) ); assign ( &ds[3][0], &(box[0][3].s[0]) ); /* 4567 face */ assign ( &ds[4][0], &(box[1][0].s[0]) ); assign ( &ds[5][0], &(box[1][1].s[0]) ); assign ( &ds[6][0], &(box[1][2].s[0]) ); assign ( &ds[7][0], &(box[1][3].s[0]) ); /* 1562 face */ assign ( &ds[1][0], &(box[2][0].s[0]) ); assign ( &ds[5][0], &(box[2][1].s[0]) ); assign ( &ds[6][0], &(box[2][2].s[0]) ); assign ( &ds[2][0], &(box[2][3].s[0]) ); /* 0473 face */ assign ( &ds[0][0], &(box[3][0].s[0]) ); assign ( &ds[4][0], &(box[3][1].s[0]) ); assign ( &ds[7][0], &(box[3][2].s[0]) ); assign ( &ds[3][0], &(box[3][3].s[0]) ); for (i=0; i<4; i++) for (j=0; j<4; j++) { pbox[i][j].cid = W_TOUCH_CID; pbox[i][j].size = W_TOUCH_SIZE; pbox[i][j].lcid = W_PBC_FRAME_CID; } pbox[2][1].lcid = 0; pbox[2][3].lcid = 0; pbox[3][1].lcid = 0; pbox[3][3].lcid = 0; while (1) { inittime = 0; nontrivial_event = False; /* Handle events accumulated in the queue */ while ( XPending(display[iw]) ) { if (inittime==0) { gettimeofday (&tp, NULL); inittime = tp.tv_sec+0.000001*tp.tv_usec; } XNextEvent (display[iw], &event); nontrivial_event = nontrivial_event || treatevent (iw, &event); } if ( !active[iw] ) { /* threads exit by themselves */ XFreeColormap (display[iw], colormap[iw]); XDestroyWindow (display[iw], windows[iw]); XCloseDisplay (display[iw]); printf ("\nWindow #%d closed on \"%s\",\n", iw, &display_addr[iw][0]); printf ("%ld frames rendered, avg %.2f frames/s.\n\n", nanim[iw], nanim[iw]/timespent[iw]); pthread_exit((void *)NULL); } if ( nontrivial_event || ((!LockOGL)&&(!OGLRendered[iw])) ) { if (inittime==0) { gettimeofday (&tp, NULL); inittime = tp.tv_sec+0.000001*tp.tv_usec; } /* <-- above ensure only essential quantity */ /* sync inessential quantity with essential one */ if (PBC[iw]) s2rw (iw, &ws[iw][0], &w[iw][0]); else rw2s (iw, &w[iw][0], &ws[iw][0]); /* below ensure both --> */ setfg (iw, W_BLACK); XFillRectangle (display[iw], pixmap[iw], gc[iw], 0, 0, win[iw].width, win[iw].height); if ( eye_aid[iw] != 2 ) { for (p=oglpool; (p->next!=NULL); p=p->next) switch (p->next->obj_type) { case W_LINE: Render_LINE(iw, p->next->n, (WLINE*)(p->next->gp)); break; case W_POINT: Render_POINT(iw, p->next->n, (WPOINT*)(p->next->gp)); break; case W_SPHERE: Render_SPHERE(iw, p->next->n, (WSPHERE*)(p->next->gp)); break; default: printf ("obj_type %d does not exist.\n", p->next->obj_type); exit(1); } /* find minimum d among real objects */ /* in order bind to the light intensity */ if (buffer_top[iw] > 0) for (min_d[iw]=W_HUGE,i=0; i<buffer_top[iw]; i++) if (min_d[iw] > buffer_d[iw][i]) min_d[iw] = buffer_d[iw][i]; } /* just objects 1: aid+objects 2: just aid */ if (eye_aid[iw]!=0) draw_eye_aids(iw); /* always draw the H-cube frame */ pbc = PBC[iw]; PBC[iw] = False; for (i=0; i<4; i++) { for (j=0; j<4; j++) { pbox[i][j].s[0] = box[i][j].s[0] + ( pbc?ws[iw][0]:0.5 ); pbox[i][j].s[1] = box[i][j].s[1] + ( pbc?ws[iw][1]:0.5 ); pbox[i][j].s[2] = box[i][j].s[2] + ( pbc?ws[iw][2]:0.5 ); } Render_POINT (iw, 4, &pbox[i][0]); } PBC[iw] = pbc; flush_buffer(iw); if (drawps[iw]) close_ps(iw); XCopyArea (display[iw], pixmap[iw], windows[iw], gc[iw], 0, 0, win[iw].width, win[iw].height, 0, 0); /* synchronize with the server without */ /* discarding events in the event queue */ XSync (display[iw], False); OGLRendered[iw] = True; nanim[iw]++; /* dynamic tuning of refresh rate */ gettimeofday (&tp, NULL); rendertime = tp.tv_sec+0.000001*tp.tv_usec-inittime; timespent[iw] += (rendertime>1./rate[iw])?rendertime:1./rate[iw]; if ( 1./rate[iw] > rendertime ) waitfor( 1./rate[iw] - rendertime ); } else waitfor( 1./rate[iw]/10. ); } } /* end Animate() */ /* Entry-point of the animator threads. */ /* Display is a connection, so can only */ /* be used by a single thread at a time */ void thread_start (void *iw_pointer) { int i, j, k, iw = *((int *)iw_pointer); /* connect to the X server */ display[iw] = XOpenDisplay(&display_addr[iw][0]); if (display[iw] == NULL) { printf ("Cannot open \"%s\", please xhost+ there.\n", &display_addr[iw][0]); exit(1); } /* get default screen */ screen[iw] = DefaultScreen (display[iw]); /* get the current color map setting */ colormap[iw] = DefaultColormap (display[iw], screen[iw]); /* allocate the named colors and their fading's */ for (i=0; i<W_MAXCOLOR; i++) { if (XAllocNamedColor(display[iw], colormap[iw], Wcolorname[i], &color[iw][i], &color[iw][i]) == 0) printf ("named color \"%s\" is not available on %s.\n", Wcolorname[i], &display_addr[iw][0]); for (k=0,j=1; j<W_COLOR_RESOLUTION; j++) { color[iw][i+j*W_MAXCOLOR].red = rint((double)color[iw][i].red*(W_COLOR_RESOLUTION-j) /W_COLOR_RESOLUTION); color[iw][i+j*W_MAXCOLOR].green = rint((double)color[iw][i].green*(W_COLOR_RESOLUTION-j) /W_COLOR_RESOLUTION); color[iw][i+j*W_MAXCOLOR].blue = rint((double)color[iw][i].blue*(W_COLOR_RESOLUTION-j) /W_COLOR_RESOLUTION); if ( i == W_BLACK-1 ) { /* for background color black it could be different */ /* color[iw][i+j*W_MAXCOLOR].red = */ /* rint( j * 65535. / W_COLOR_RESOLUTION ); */ /* color[iw][i+j*W_MAXCOLOR].green = */ /* rint( j * 65535. / W_COLOR_RESOLUTION ); */ /* color[iw][i+j*W_MAXCOLOR].blue = */ /* rint( j * 65535. / W_COLOR_RESOLUTION ); */ } k += (XAllocColor(display[iw], colormap[iw], &color[iw][i+j*W_MAXCOLOR])==0)?1:0; } if ( k!=0 ) { printf ("Fading's of color \"%s\" has %d unsuccessful\n", Wcolorname[i], k); printf ("instances in color resolution = %d on %s.\n", W_COLOR_RESOLUTION, &display_addr[iw][0]); } } /* black and white on current server */ background[iw] = BlackPixel (display[iw], screen[iw]); foreground[iw] = WhitePixel (display[iw], screen[iw]); /* root window */ root[iw] = DefaultRootWindow (display[iw]); windows[iw] = XCreateSimpleWindow ( display[iw], root[iw], 100, 100, win[iw].width+1, win[iw].height+1, border_width[iw], foreground[iw], background[iw] ); if ( (void *)windows[iw] == NULL ) { printf ("Cannot open window %d on \"%s\"\n", iw, &display_addr[iw][0]); exit(1); } XSetStandardProperties ( display[iw], windows[iw], &title[iw][0], &title[iw][0], None, NULL, 0, &win[iw] ); /* event filter */ XSelectInput ( display[iw], windows[iw], ButtonPressMask | ButtonReleaseMask | Button1MotionMask | KeyPressMask | ExposureMask ); /* pop this window up on the screen */ XMapRaised (display[iw], windows[iw]); XGetGeometry ( display[iw], windows[iw], &root[iw], &win[iw].x, &win[iw].y, (unsigned *) &win[iw].width, (unsigned *) &win[iw].height, &border_width[iw], &color_depth[iw] ); win[iw].width--; win[iw].height--; /* pixmap instead of window for back-buffer drawing */ pixmap[iw] = XCreatePixmap ( display[iw], root[iw], win[iw].width, win[iw].height, color_depth[iw] ); /* magic sphere size is about the size of the tunnel */ mgs_size[iw] = W_GOLDEN_RATIO * sqrt((double)win[iw].width*win[iw].height/W_PI); gc[iw] = XCreateGC (display[iw], pixmap[iw], 0, 0); Animate (iw); } /* end thread_start() */ int WAddDefaultWindow (double h[3][3]) { return( WAddWindow ("W_DEFAULT", /* $DISPLAY as xhost */ "W_DEFAULT", /* "win iw" as title */ (volatile double(*)[3]) h, /* metric of rs */ W_DEFAULT, /* rs->rw unit */ W_DEFAULT_PBC, /** PBC, no default key **/ W_DEFAULT, W_DEFAULT, /* viewport origin xy in rw */ W_DEFAULT, /* viewport origin z in rw */ W_DEFAULT, W_DEFAULT, W_DEFAULT, /* x-axes direction */ W_DEFAULT, W_DEFAULT, W_DEFAULT, /* y-axes direction */ W_DEFAULT, W_DEFAULT, /* screen size contains box */ W_DEFAULT, /* eye position */ W_DEFAULT, /* eye thickness factor */ W_DEFAULT, /* view-plate thickness */ W_DEFAULT, /* Z-buffer order (and lighting) scheme */ W_DEFAULT, W_DEFAULT, /* lighting parameters alpha and beta */ W_DEFAULT, /* factor of change */ W_DEFAULT_EYE_AID, /* draw the eye_aide or not */ W_DEFAULT /* animation frames/second */) ); } /* end WAddDefaultWindow() */ /* Create a new window with reasonable defaults */ int WAddWindow (char waddress[], char wtitle[], volatile double h[3][3], double wunit, Bool wpbc, double wwx, double wwy, double wwz, double wxx, double wxy, double wxz, double wyx, double wyy, double wyz, double wwidth, double wheight, double weye, double weye_thickness_factor, double wplate_thickness, int worder_scheme, double walpha, double wbeta, double wfactor, Bool weye_aid, double wrefreshrate) { /* This is static because &iw is going */ static int iw; /* to be used by the new thread */ pthread_t tid; if ( A_never_entered ) SignIn (h); else if ( H!=h ) { printf ("fatal error: H matrix storage has changed.\n"); exit(1); } /* find an occupied slot */ for (iw=0; (iw<W_MAXWINDOW)&&active[iw]; iw++); if ( iw == W_MAXWINDOW ) { printf ("W_MAXWINDOW = %d reached (see \"W.h\").\n", W_MAXWINDOW); return (0); } /* check incoming string lengths */ if ( (strlen(waddress) >= W_MAX_STRLEN) || (strlen(wtitle) >= W_MAX_STRLEN) ) { printf ("W_MAX_STRLEN = %d reached (see \"W.h\").\n", W_MAX_STRLEN); exit(1); } /* store the TCP/IP X connection address */ strcpy (&display_addr[iw][0], waddress); if (!strcasecmp(&display_addr[iw][0], "W_DEFAULT")) strcpy (&display_addr[iw][0], getenv("DISPLAY")); /* store the window title name */ strcpy (&title[iw][0], wtitle); if (!strcasecmp(&title[iw][0], "W_DEFAULT")) sprintf (&title[iw][0], "win %d", iw); /* how much rs length is 1 render world (rw) unit */ unit[iw] = key_verify(double, wunit, W_DEFAULT)? pow(fabs(h[0][0]*(h[1][1]*h[2][2]-h[2][1]*h[1][2]) - h[0][1]*(h[1][0]*h[2][2]-h[1][2]*h[2][0]) + h[0][2]*(h[1][0]*h[2][1]-h[1][1]*h[2][0])),1./3.) :wunit; /* the world is PBC or not */ PBC[iw] = wpbc; /* where to put the window -- center and above */ w[iw][0] = key_verify(double, wwx, W_DEFAULT)? 0.5*(h[0][0]+h[1][0]+h[2][0])/unit[iw]:wwx; w[iw][1] = key_verify(double, wwy, W_DEFAULT)? 0.5*(h[0][1]+h[1][1]+h[2][1])/unit[iw]:wwy; w[iw][2] = key_verify(double, wwz, W_DEFAULT)? 1.5*(fabs(h[0][2])+fabs(h[1][2])+fabs(h[2][2]))/unit[iw] :wwz; /* if PBC then ws[] is an essential quantity */ if (PBC[iw]) rw2s (iw, &w[iw][0], &ws[iw][0]); /* what is the window x-axis in rw */ A[iw][0][0] = key_verify(double, wxx, W_DEFAULT)? 1:wxx; A[iw][0][1] = key_verify(double, wxy, W_DEFAULT)? 0:wxy; A[iw][0][2] = key_verify(double, wxz, W_DEFAULT)? 0:wxz; normalize (&A[iw][0][0]); /* what is the window y-axis in rw */ A[iw][1][0] = key_verify(double, wyx, W_DEFAULT)? 0:wyx; A[iw][1][1] = key_verify(double, wyy, W_DEFAULT)? 1:wyy; A[iw][1][2] = key_verify(double, wyz, W_DEFAULT)? 0:wyz; /* calculate the window z-axis */ cross (&A[iw][0][0], &A[iw][1][0], &A[iw][2][0]); normalize (&A[iw][2][0]); /* re-calculate the window y-axis */ cross (&A[iw][2][0], &A[iw][0][0], &A[iw][1][0]); normalize (&A[iw][1][0]); /* screen contains H-cube */ win[iw].width = W_RESOLUTION * (key_verify(double, wwidth, W_DEFAULT)? (fabs(h[0][0])+fabs(h[1][0])+fabs(h[2][0]))/unit[iw] :wwidth); win[iw].height = W_RESOLUTION * (key_verify(double, wheight, W_DEFAULT)? (fabs(h[0][1])+fabs(h[1][1])+fabs(h[2][1]))/unit[iw] :wheight); /* set X properties */ win[iw].flags = PSize; /* eye position */ eye[iw] = key_verify(double, weye, W_DEFAULT)? W_DEFAULT_EYE :fabs(weye); /* eye thickness factor: initially at the eye */ eye_thickness_factor[iw] = key_verify(double, weye_thickness_factor, W_DEFAULT)? 0.8 :weye_thickness_factor; /* view-plate thickness, enough to contain the tube */ plate_thickness[iw] = key_verify(double, wplate_thickness, W_DEFAULT)? 2.5*(fabs(h[0][2])+fabs(h[1][2])+fabs(h[2][2]))/unit[iw] :fabs(wplate_thickness); /* Z-buffer order (and lighting) scheme */ order[iw] = key_verify(int, worder_scheme, W_DEFAULT)? W_DEFAULT_ORDER :worder_scheme; min_d[iw] = W_HUGE; /* lighting parameters alpha and beta */ alpha[iw] = key_verify(double, walpha, W_DEFAULT)? W_DEFAULT_ALPHA :walpha; beta[iw] = key_verify(double, wbeta, W_DEFAULT)? W_DEFAULT_BETA :wbeta; /* factor of change */ factor[iw] = key_verify(double, wfactor, W_DEFAULT)? 0.1 :wfactor; /* draw the eye aid or not */ eye_aid[iw] = weye_aid; /* pictures per second */ rate[iw] = key_verify(double, wrefreshrate, W_DEFAULT)? W_DEFAULT_REFRESHRATE :fabs(wrefreshrate); /* rotation pivot */ pivot[iw] = W_OBJECTS_PIVOT; /* initialize internal counters for this window */ set_initial_counters(iw); active[iw] = True; /* spawn a thread to animate and handle events */ pthread_create (&tid, NULL, (void *(*)(void *)) thread_start, (void *)(&iw)); return (iw); } /* end WAddWindow() */ void WCloseWindow (int iw) { int i; Bool one_active = False; active[iw] = False; for (i=0; i<W_MAXWINDOW; i++) one_active = one_active || active[i]; if (!one_active) SignOut(); return; } /* end WCloseWindow() */ void WCloseAllWindows() { int i; for (i=0; i<W_MAXWINDOW; i++) WCloseWindow(i); return; } /* end VCloseAllWindows() */ /* register in Object Group List */ /* and allocate space for group. */ void *WCreateOG (char token[], int obj_type, int n) { struct OGL *p, *q; if (strlen(token)>W_MAX_OG_TOKEN_SIZE-1) { printf ("token %s should be less than %d bytes long.\n", token, W_MAX_OG_TOKEN_SIZE); exit(1); } /* root node is oglpool[0] */ for ( p = oglpool; (p->next != NULL) && strncmp(p->next->token, token, W_MAX_OG_TOKEN_SIZE-1); p = p->next ); if ( p->next == NULL ) { /* look for free space */ for ( q = oglpool+1; q < oglpool+W_MAX_NUM_OGS; q++ ) if ( *(q->token)==(char)0 ) { p->next = q; q->next = NULL; strcpy (q->token, token); q->obj_type = obj_type; q->n = n; q->gp = (void *) malloc (n*W_OBJ_SIZE(obj_type)); /* set defaults to 0 */ memset (q->gp, 0, n*W_OBJ_SIZE(obj_type)); return (q->gp); } printf ("error: oglpool %d records all used up.\n", W_MAX_NUM_OGS-1); exit(1); } else { printf("error: OG token name \"%s\" already used.\n", token); exit(1); } return (NULL); } /* WCreateOG() */ void *WGetOG (char token[]) { struct OGL *p; for ( p = oglpool; (p->next != NULL) && strncmp(p->next->token, token, W_MAX_OG_TOKEN_SIZE-1); p = p->next); if ( p->next == NULL ) { printf("error: OG token name \"%s\" not found.\n", token); exit(1); } else return (p->next->gp); return (NULL); } /* WGetOG() */ void WDeleteOG (char token[]) { struct OGL *p; /* destroy this record and free resources */ for ( p = oglpool; (p->next != NULL) && strncmp(p->next->token, token, W_MAX_OG_TOKEN_SIZE-1); p = p->next); if ( p->next == NULL ) { printf("error: token name \"%s\" not found.\n", token); exit(1); } else { free (p->next->gp); *(p->next->token) = (char)0; p->next = p->next->next; return; } } /* WDeleteOG() */ int WDeleteAllOGs() { int i, n = 0; struct OGL *q; for ( q = oglpool+1; q < oglpool+W_MAX_NUM_OGS; q++ ) if ( *(q->token)!=(char)0 ) { /* free resources */ n++; free (q->gp); } memset (oglpool, 0, W_MAX_NUM_OGS*sizeof(struct OGL)); return (n); } /* WDeleteOG() */ /* Animators are not allowed to render when */ /* the OG is being modified by main thread. */ void WLock() { LockOGL = True; return; } /* end WLock() */ /* Unlock when done and clean the slates */ void WUnLock() { int i; for (i=0; i<W_MAXWINDOW; i++) OGLRendered[i] = False; LockOGL = False; return; } /* end WUnLock() */ #ifndef _NO_W_TEST void *colorful (char name[], int n) { int i; WSPHERE *sphere; WLock(); sphere = (WSPHERE *) WCreateOG (name, W_SPHERE, n); for (i=0; i<n; i++) { sphere[i].s[0] = frandom(); sphere[i].s[1] = frandom(); sphere[i].s[2] = frandom(); sphere[i].radius = 0.25; sphere[i].cid = W_BLACK+1+ floor((W_WHITE-W_BLACK)*frandom()); } WUnLock(); return ((void *)sphere); } /* end colorful */ #define SI_RADIUS (0.3) #define C_RADIUS (SI_RADIUS/2) void *SiC (char name[], int m) { int i, j, k, l, p; WSPHERE *sphere; WLock(); sphere = (WSPHERE *) WCreateOG (name, W_SPHERE, m*m*m*4*2); for (i=0; i<m; i++) for (j=0; j<m; j++) for (k=0; k<m; k++) { l = 8*(i*m*m+j*m+k); sphere[l].s[0] = (1./8+i)/m; sphere[l].s[1] = (1./8+j)/m; sphere[l].s[2] = (1./8+k)/m; sphere[l].radius = SI_RADIUS; sphere[l].cid = W_GREEN; sphere[l+1].s[0] = (1./8+i)/m; sphere[l+1].s[1] = (5./8+j)/m; sphere[l+1].s[2] = (5./8+k)/m; sphere[l+1].radius = SI_RADIUS; sphere[l+1].cid = W_GREEN; sphere[l+2].s[0] = (5./8+i)/m; sphere[l+2].s[1] = (1./8+j)/m; sphere[l+2].s[2] = (5./8+k)/m; sphere[l+2].radius = SI_RADIUS; sphere[l+2].cid = W_GREEN; sphere[l+3].s[0] = (5./8+i)/m; sphere[l+3].s[1] = (5./8+j)/m; sphere[l+3].s[2] = (1./8+k)/m; sphere[l+3].radius = SI_RADIUS; sphere[l+3].cid = W_GREEN; for (p=0; p<4; p++) { sphere[l+4+p].s[0] = sphere[l+p].s[0] + 1./4/m; sphere[l+4+p].s[1] = sphere[l+p].s[1] + 1./4/m; sphere[l+4+p].s[2] = sphere[l+p].s[2] + 1./4/m; sphere[l+4+p].radius = C_RADIUS; sphere[l+4+p].cid = W_RED; } } WUnLock(); return ((void *)sphere); } /* end SiC() */ void bond_it (double H[3][3], int n, WSPHERE *sphere, int *atomlist, double bondlength, WLINE *bond, Bool rookie) { int i, j, l; double ds[3], dx[3], dr2, dr; if (rookie) for (l=i=0; i<n; i++) for (j=i+1; j<n; j++) { ds[0] = sphere[j].s[0] - sphere[i].s[0]; ds[1] = sphere[j].s[1] - sphere[i].s[1]; ds[2] = sphere[j].s[2] - sphere[i].s[2]; s_truncate (ds); dx[0] = ds[0]*H[0][0]+ds[1]*H[1][0]+ds[2]*H[2][0]; dx[1] = ds[0]*H[0][1]+ds[1]*H[1][1]+ds[2]*H[2][1]; dx[2] = ds[0]*H[0][2]+ds[1]*H[1][2]+ds[2]*H[2][2]; if ( (dr2=dot(dx,dx)) < bondlength*bondlength ) { if ( l>=2*n ) return; dr = sqrt(dr2); atomlist[2*l] = i; atomlist[2*l+1] = j; bond[l].s[0] = sphere[i].s[0] + ds[0] * sphere[i].radius / dr; bond[l].s[1] = sphere[i].s[1] + ds[1] * sphere[i].radius / dr; bond[l].s[2] = sphere[i].s[2] + ds[2] * sphere[i].radius / dr; bond[l].s[3] = sphere[j].s[0] - ds[0] * sphere[j].radius / dr; bond[l].s[4] = sphere[j].s[1] - ds[1] * sphere[j].radius / dr; bond[l].s[5] = sphere[j].s[2] - ds[2] * sphere[j].radius / dr; bond[l].cid = W_WHITE; l++; } } else for (l=0; l<2*n; l++) { i=atomlist[2*l]; j=atomlist[2*l+1]; ds[0] = sphere[j].s[0] - sphere[i].s[0]; ds[1] = sphere[j].s[1] - sphere[i].s[1]; ds[2] = sphere[j].s[2] - sphere[i].s[2]; s_truncate (ds); dx[0] = ds[0]*H[0][0]+ds[1]*H[1][0]+ds[2]*H[2][0]; dx[1] = ds[0]*H[0][1]+ds[1]*H[1][1]+ds[2]*H[2][1]; dx[2] = ds[0]*H[0][2]+ds[1]*H[1][2]+ds[2]*H[2][2]; atomlist[2*l] = i; atomlist[2*l+1] = j; dr2=dot(dx,dx); dr = sqrt(dr2); bond[l].s[0] = sphere[i].s[0] + ds[0] * sphere[i].radius / dr; bond[l].s[1] = sphere[i].s[1] + ds[1] * sphere[i].radius / dr; bond[l].s[2] = sphere[i].s[2] + ds[2] * sphere[i].radius / dr; bond[l].s[3] = sphere[j].s[0] - ds[0] * sphere[j].radius / dr; bond[l].s[4] = sphere[j].s[1] - ds[1] * sphere[j].radius / dr; bond[l].s[5] = sphere[j].s[2] - ds[2] * sphere[j].radius / dr; } return; } /* end bond_it() */ void *testline (char *name) { WLINE *line; WLock(); line = (WLINE *) WCreateOG (name, W_LINE, 1); line[0].s[0] = 0.1; line[0].s[1] = 0.5; line[0].s[2] = 1.0; line[0].s[3] = 0.9; line[0].s[4] = 0.5; line[0].s[5] = 1.0; line[0].cid = W_WHITE; WUnLock(); return ((void *)line); } /* end testline() */ void *testpoint (char *name) { WPOINT *point; WLock(); point = (WPOINT *) WCreateOG (name, W_POINT, 2); point[0].s[0] = 0.2; point[0].s[1] = 0.1; point[0].s[2] = 0.3; point[0].size = 15; point[0].cid = W_GREEN; point[0].lcid = W_WHITE; point[1].s[0] = 0.8; point[1].s[1] = 0.9; point[1].s[2] = 0.2; point[1].size = 8; point[1].cid = W_RED; WUnLock(); return ((void *)point); } /* end testpoint() */ #define LATTICE 3 #define CONSTANT 4.3 #define N (8*LATTICE*LATTICE*LATTICE) void main() { double t, e, a, H[3][3] = {{LATTICE*CONSTANT, 0, 0}, {0, LATTICE*CONSTANT, 0}, {0, 0, LATTICE*CONSTANT}}, vector[7*N]; int i, atomlist[4*N]; Bool second = False; /* WLINE *line = (WLINE *) testline("line"); */ /* WPOINT *test_point = (WPOINT *) testpoint("testpoint"); */ WSPHERE *sphere; WLINE *bond; WAddDefaultWindow (H); srandom(time(0)); watch ("animation", 0); while ( (t=watch("animation",1)) < 3600. ) { if ( (!second) && (watch("animation",1)>0.01) ) { WLock(); sphere = (WSPHERE *) SiC ("colorful", LATTICE); #define BONDLENGTH (CONSTANT*sqrt(3.)/4*(1+W_TINY)) bond = (WLINE *) WCreateOG ("bond", W_LINE, 2*N); bond_it (H, N, sphere, atomlist, BONDLENGTH, bond, True); #define MOTION (0./LATTICE) /* #define MOTION (0.15/LATTICE) */ for (i=0; i<N; i++) { vector[7*i] = 2.+frandom()*3; vector[7*i+1] = (frandom()-0.5) * MOTION; vector[7*i+2] = (frandom()-0.5) * MOTION; vector[7*i+3] = (frandom()-0.5) * MOTION; vector[7*i+4] = sphere[i].s[0]; vector[7*i+5] = sphere[i].s[1]; vector[7*i+6] = sphere[i].s[2]; } WUnLock(); second = True; } if (second) { WLock(); #define PERIOD (2.5) #define AMPLITUDE (0.035) /* #define AMPLITUDE (0.0) */ e = AMPLITUDE * sin(t/PERIOD*2*W_PI); H[0][0] = LATTICE*CONSTANT*sqrt(1+2*e); H[1][1] = LATTICE*CONSTANT/sqrt(sqrt(1+2*e)); H[2][2] = LATTICE*CONSTANT/sqrt(sqrt(1+2*e)); for (i=0; i<N; i++) { a = sin (t/vector[7*i]*2*W_PI); sphere[i].s[0] = vector[7*i+4] + vector[7*i+1]*a; sphere[i].s[1] = vector[7*i+5] + vector[7*i+2]*a; sphere[i].s[2] = vector[7*i+6] + vector[7*i+3]*a; } bond_it (H, N, sphere, atomlist, BONDLENGTH, bond, False); WUnLock(); } waitfor(0.001); } printf ("Master process time up.\n"); WCloseAllWindows(); printf ("total of %d OGs deleted.\n", WDeleteAllOGs()); return; } /* end main() */ #endif /* Version Logs: ------------------- 1.0 (Nov.17 1998) Multiple displays; Threaded animation (not available on Linux); Parallel perspective; Window clipping; Dynamic frame-rate; Xpm screenshots. ------------------- 2.0 (Jan.11 1999) Linear+aerial perspective; Full 3D navigation; Periodic boundary conditions; Approximate z-clipping; Postscript screenshots. ------------------- 2.01 (Mar.2 1999) Modify the signal mechanism so it's now thread-safe; Check it works for RedHat Linux 5.2. */ /* Notes: 1. For the user, an animator life cycle begins when he adds the first window, and ends when no window is currently active; the animator is then reusable as if new. The code achieves this by SignIn() at birth and SignOut() at death; individual windows are initialized by set_initial_counters(int iw), and die by setting active[iw] = False. The only static bootstrapping variable needed to be preset for the Animator is "A_never_entered". The OGL data structure is entirely independent of the animator, and thus also needs to be preset. 2. Animation policy: a window only needs to be rendered when A) OGLRendered[iw]=False and LockOGL=False, meaning there is a new scene yet hasn't been rendered and the main thread has finished putting up the objects (revising OGL data). The flags are set by WUnLock(). B) when there are mouse/key events that are "non_trivial", so the scene has to be re-rendered because some observer parameters have changed. In either case, we measure the time spent on rendering + possible prior event-handling for the new frame: if this time is smaller than 1/rate[iw], we sleep a little so the time spent for this frame is 1/rate[iw]; otherwise we must suffer a "time deficit". So the avg. frame rate is always smaller than rate[iw]. 3. Viewport origin can be specified by both w[] and ws[]: PBC Not PBC ---------------------------------------- ws[] constant vary with H w[] vary with H constant ---------------------------------------- Those which stay constant are called essential quantities. Events (mouse,keyboard) are always handled before the scene is rendered, and event handlers are only responsible for modifying the essential quantities. But just prior to rendering the scene, it is the responsibility of Animate() to "sync" the inessential with essential quantities to make rendering subroutines (such as s2screen) easier. 4. Under PBC, the world is made of infinite replicas of H cubes. But we shall only render those objects that are inside (-0.5,0.5) * H with center at ws[]. */