/***************************************/ /* 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_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-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; ix0+bx)?1:0) + ((ym=x[1]+c*dx[1]y0+by)?1:0) + ((zm=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 (zz1, 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/k0)) { 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 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; i0)) 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 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 %.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_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; irw 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_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; iW_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=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