/* sulfate2-pm.c 1.1 */ /* Copyright 1992, 1993 O'Reilly and Associates, Inc. Permission to use, copy, and modify this program is hereby granted, as long as this copyright notice appears in each copy of the program source code. */ #include "book_utils.h" static Pixmap pixmap; static XWindowAttributes wattrs; static GC gc; #define DEG_TO_RAD (M_PI/180) /* Atom and ion dimensions, all in Angstroms */ #define S_RADIUS 1.04 #define O_RADIUS 0.66 #define SO_BOND_LENGTH 1.44 #define SO_LENGTH (S_RADIUS + SO_BOND_LENGTH + O_RADIUS) /* Utility function to build a wireframe sphere. */ static void build_sphere(); /* The structure ids. */ static PEXStructure model, sulfate, sulfur, oxygen, sphere; /* The atom locations are in spherical coordinates. */ typedef struct { float r, t, p; /* radius, theta, phi */ } Sphere_point; static Sphere_point pos[] = { {SO_LENGTH, 0, 0}, {SO_LENGTH, 0, (109 * DEG_TO_RAD)}, {SO_LENGTH, (120 * DEG_TO_RAD), (109 * DEG_TO_RAD)}, {SO_LENGTH, (240 * DEG_TO_RAD), (109 * DEG_TO_RAD)} }; void create_model( dpy ) Display *dpy; { PEXListOfCoord sphere_pts; PEXCoord bond_points[2], fp; PEXColor color; PEXMatrix translate, scale, rotate, global; PEXVector tr, sc; int i; /*** Create the sphere structure. ***/ sphere = PEXCreateStructure( dpy ); build_sphere( 1.0, &sphere_pts ); PEXPolyline( dpy, sphere, PEXOCStore, sphere_pts.count, sphere_pts.points ); /*** Create the sulfur atom. ***/ sulfur = PEXCreateStructure( dpy ); SET_COLOR( 1, 1, 0, color ) PEXSetLineColor( dpy, sulfur, PEXOCStore, PEXColorTypeRGB, &color ); /* Scale and execute the sphere. */ sc.x = sc.y = sc.z = S_RADIUS; PEXScale( &sc, scale ); PEXSetLocalTransform( dpy, sulfur, PEXOCStore, PEXReplace, scale ); PEXExecuteStructure( dpy, sulfur, PEXOCStore, sphere ); /*** Create the oxygen atom. ***/ oxygen = PEXCreateStructure( dpy ); SET_COLOR( 0, 1, 1, color ) PEXSetLineColor( dpy, oxygen, PEXOCStore, PEXColorTypeRGB, &color ); /* Scale and execute the sphere. */ sc.x = sc.y = sc.z = O_RADIUS; PEXScale( &sc, scale ); PEXSetLocalTransform( dpy, oxygen, PEXOCStore, PEXReplace, scale ); PEXExecuteStructure( dpy, oxygen, PEXOCStore, sphere ); /*** Create the sulfate structure. ***/ sulfate = PEXCreateStructure( dpy ); /* Instance the Sulfur atom. */ PEXExecuteStructure( dpy, sulfate, PEXOCStore, sulfur ); /* Position and instance the oxygen atoms. */ for ( i = 0; i < 4; i++ ) { /* Convert the position to Cartesion coordinates. */ tr.x = pos[i].r * sin( pos[i].t ) * sin( pos[i].p ); tr.y = pos[i].r * cos( pos[i].p ); tr.z = pos[i].r * cos( pos[i].t ) * sin( pos[i].p ); /* Build the matrix and create the transform element. */ PEXTranslate( &tr, translate ); PEXSetLocalTransform( dpy, sulfate, PEXOCStore, PEXReplace, translate ); /* Instance the oxygen structure. */ PEXExecuteStructure( dpy, sulfate, PEXOCStore, oxygen ); } /* Build the bonds. */ SET_COLOR( 1, 0, 0, color ) PEXSetLineColor( dpy, sulfate, PEXOCStore, PEXColorTypeRGB, &color ); bond_points[0].x = bond_points[0].z = 0; bond_points[0].y = S_RADIUS; bond_points[1].x = bond_points[1].z = 0; bond_points[1].y = S_RADIUS + SO_BOND_LENGTH; for ( i = 0; i < 4; i++ ) { PEXRotate( PEXYAxis, pos[i].t, rotate ); PEXSetLocalTransform( dpy, sulfate, PEXOCStore, PEXReplace, rotate ); PEXRotate( PEXXAxis, pos[i].p, rotate ); PEXSetLocalTransform( dpy, sulfate, PEXOCStore, PEXPreConcatenate, rotate ); PEXPolyline( dpy, sulfate, PEXOCStore, 2, bond_points ); } /*** Build the top-level structure. ***/ model = PEXCreateStructure( dpy ); /* Use the global transform to scale and center the ion. */ sc.x = sc.y = sc.z = 1 / (3 * SO_LENGTH); tr.x = tr.y = tr.z = 0.5; fp.x = fp.y = fp.z = 0; PEXBuildTransform( &fp, &tr, 0.0, 0.0, 0.0, &sc, global ); PEXSetGlobalTransform( dpy, model, PEXOCStore, global ); /* Use a local transform to rotate the model. */ PEXIdentityMatrix( rotate ); PEXSetLocalTransform( dpy, model, PEXOCStore, PEXReplace, rotate ); /* Execute the sulfate ion. */ PEXExecuteStructure( dpy, model, PEXOCStore, sulfate ); } #define NUM_LAT_SEGS 20 #define NUM_LON_SEGS 30 static void build_sphere( radius, points ) float radius; PEXListOfCoord *points; { /* Build a "wire" sphere, using a single spiral polyline. */ double phi, delta_phi, theta, delta_theta; unsigned long i; points->count = NUM_LAT_SEGS * NUM_LON_SEGS + 1; points->points = (PEXCoord *) malloc( (unsigned) (points->count * sizeof(PEXCoord)) ); phi = 0; /* start at the North pole */ delta_phi = M_PI / (NUM_LAT_SEGS * NUM_LON_SEGS); theta = 0; delta_theta = (2 * M_PI) / NUM_LON_SEGS; for ( i = 0; i < points->count; i++ ) { points->points[i].x = radius * sin( phi ) * sin( theta ); points->points[i].y = radius * cos( phi ); points->points[i].z = radius * sin( phi ) * cos( theta ); theta += delta_theta; phi += delta_phi; } } static void redraw( dpy, window, renderer ) Display *dpy; Window window; PEXRenderer renderer; { /* Clear the off-screen pixmap. */ XFillRectangle( dpy, pixmap, gc, 0, 0, wattrs.width, wattrs.height ); PEXRenderNetwork( dpy, pixmap, renderer, model ); /* Make the pixmap visible. */ XCopyArea( dpy, pixmap, window, gc, 0, 0, wattrs.width, wattrs.height, 0, 0 ); XFlush( dpy ); } #define WIN_GEOM "500x500+50+50" main( argc, argv ) int argc; char *argv[]; { Display *dpy; Window window; XEvent event; XVisualInfo vis_info; XColor bkgd_color; XStandardColormap cmap_info; PEXColorApproxEntry capx_info; PEXRenderer renderer; PEXRendererAttributes rattrs; PEXExtensionInfo *pexinfo; int done = 0; XGCValues gcattrs; /* Open a display and initialize PEX. */ dpy = ora_init_pex( argv, &pexinfo ); if ( !dpy ) exit(1); /* Determine the best visual to use. */ ora_find_best_visual( dpy, &vis_info ); /* Get a standard colormap for the visual. */ if ( !ora_get_standard_colormap( dpy, &vis_info, &cmap_info ) ) { fprintf ( stderr, "Cannot find a standard colormap\n" ); exit(1); } ora_set_stdcmap_approx( &vis_info, &cmap_info, &capx_info ); /* Create the window, giving it a gray background. */ bkgd_color.red = bkgd_color.green = bkgd_color.blue = 0x5555; bkgd_color.flags = DoRed | DoGreen | DoBlue; window = ora_create_window( dpy, &vis_info, &cmap_info, WIN_GEOM, &bkgd_color ); if ( STRUCTURE_SPT( pexinfo ) ) { renderer = ora_setup_renderer( dpy, window, &capx_info, &rattrs ); create_model( dpy ); } else { fprintf( stderr, "No structure mode.\n" ); exit(4); } /* Create a pixmap for double buffering. */ XGetWindowAttributes( dpy, window, &wattrs ); pixmap = XCreatePixmap( dpy, window, wattrs.width, wattrs.height, wattrs.depth ); if ( pixmap == NULL ) { fprintf( stderr, "Can't allocate a pixmap.\n" ); exit(4); } /* Create a graphics context and set the foreground color. */ gcattrs.foreground = bkgd_color.pixel; gc = XCreateGC( dpy, window, GCForeground, &gcattrs ); /* Read and respond to X events. */ XSelectInput( dpy, window, ExposureMask | ButtonPressMask ); while ( !done ) { XNextEvent( dpy, &event ); switch ( event.type ) { case Expose: /* Flush remaining Expose events, then redraw. */ while ( XCheckTypedWindowEvent( dpy, window, Expose, &event ) ) ; /* empty statement */ redraw( dpy, window, renderer ); break; case ButtonPress: switch ( event.xbutton.button ) { case Button1: rotate_ion( dpy, window, renderer ); break; default: done = 1; break; } break; } } XCloseDisplay( dpy ); return 0; } static int rotate_ion( dpy, window, renderer ) Display *dpy; Window window; PEXRenderer renderer; { /* Use the modeling transform in the model structure to rotate */ /* the ion: Open the structure, set the edit mode to REPLACE, */ /* position the element pointer to the Set Local Transform */ /* element, then replace the element. */ double angle; PEXMatrix rotate; PEXSetEditingMode( dpy, model, PEXStructureReplace ); PEXSetElementPtr( dpy, model, PEXBeginning, 2 ); for ( angle = M_PI/100; angle <= 2*M_PI; angle += M_PI/100 ) { PEXRotate( PEXXAxis, angle, rotate ); PEXSetLocalTransform( dpy, model, PEXOCStore, PEXReplace, rotate ); redraw( dpy, window, renderer ); } }