/* pvt-mb.c 1.2 */ /* 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" #include "mbx.h" /* Globals. */ static PEXStructure model, surface, borders; static Display *dpy; static Window window; static PEXRenderer renderer; static Multibuffer buffers[2], cur_buf; /* The view table and view table entry. */ static PEXLookupTable view_table; static PEXViewEntry view; /* The view orientation parameters. */ static PEXCoord vrp; static PEXVector vup, vpn; /* Constants */ #define PAN_SCALE (M_PI/500) /* Define the projection viewports for the four views. */ static PEXNPCSubVolume vports[] = { /* x-min, y-min, z-min, x-max, y-max, z-max */ {{ 0.5, 0.5, 0},{ 1, 1, 1}}, /* view 1 */ {{ 0.5, 0, 0},{ 1, 0.5, 1}}, /* view 2 */ {{ 0, 0, 0},{ 0.5, 0.5, 1}}, /* view 3 */ {{ 0, 0.5, 0},{ 0.5, 1, 1}} /* view 4 */ }; static void initialize_views() { PEXCoord2D view_window[2]; double view_plane, front_plane, back_plane; PEXCoord prp; PEXNPCSubVolume viewport; int err, perspective; /* Set the view paramters that most views use. */ vrp.x = 0.5; vrp.y = 0.5; vrp.z = 0.5; vup.x = 0; vup.y = 1; vup.z = 0; perspective = False; view_plane = 0; prp.x = 0; prp.y = 0; prp.z = 10; view_window[0].x = -1; view_window[1].x = 1; view_window[0].y = -1; view_window[1].y = 1; back_plane = -1; front_plane = 1; view.clip_flags = PEXClippingAll; /* Right-side -- view 2. Put it in lower-right corner. */ vpn.x = 1; vpn.y = 0; vpn.z = 0; err = PEXViewOrientationMatrix( &vrp, &vpn, &vup, view.orientation ); err = PEXViewMappingMatrix( view_window, &vports[1], perspective, &prp, view_plane, back_plane, front_plane, view.mapping ); view.clip_limits = vports[1]; PEXSetTableEntries( dpy, view_table, 2, 1, PEXLUTView, &view ); /* Front -- view 3. Put it in lower-left corner. */ vpn.x = 0; vpn.y = 0; vpn.z = 1; err = PEXViewOrientationMatrix( &vrp, &vpn, &vup, view.orientation ); err = PEXViewMappingMatrix( view_window, &vports[2], perspective, &prp, view_plane, back_plane, front_plane, view.mapping ); view.clip_limits = vports[2]; PEXSetTableEntries( dpy, view_table, 3, 1, PEXLUTView, &view ); /* Top -- view 4. Put it in upper-left corner. */ vpn.x = 0; vpn.y = 1; vpn.z = 0; vup.x = 0; vup.y = 0; vup.z = -1; err = PEXViewOrientationMatrix( &vrp, &vpn, &vup, view.orientation ); err = PEXViewMappingMatrix( view_window, &vports[3], perspective, &prp, view_plane, back_plane, front_plane, view.mapping ); view.clip_limits = vports[3]; PEXSetTableEntries( dpy, view_table, 4, 1, PEXLUTView, &view ); /* Off-axis -- view 1. Put it in upper-right corner. */ vpn.x = 1; vpn.y = 0.1; vpn.z = 1; vup.x = 0; vup.y = 1; vup.z = 0; err = PEXViewOrientationMatrix( &vrp, &vpn, &vup, view.orientation ); err = PEXViewMappingMatrix( view_window, &vports[0], perspective, &prp, view_plane, back_plane, front_plane, view.mapping ); view.clip_limits = vports[0]; PEXSetTableEntries( dpy, view_table, 1, 1, PEXLUTView, &view ); } static void create_borders() { PEXCoord2D pts[5]; PEXColor color; int i; borders = PEXCreateStructure( dpy ); /* Use view 0 for the borders. */ PEXSetViewIndex( dpy, borders, PEXOCStore, 0 ); /* Draw a polyline around each viewport. */ SET_COLOR( 1, 1, 1, color ); /* white */ PEXSetLineColor( dpy, borders, PEXOCStore, PEXColorTypeRGB, &color ); PEXSetLineWidth( dpy, borders, PEXOCStore, 2.0 ); for ( i = 0; i < 4; i++ ) { pts[0].x = vports[i].min.x; pts[0].y = vports[i].min.y; pts[1].x = vports[i].max.x; pts[1].y = vports[i].min.y; pts[2].x = vports[i].max.x; pts[2].y = vports[i].max.y; pts[3].x = vports[i].min.x; pts[3].y = vports[i].max.y; pts[4].x = vports[i].min.x; pts[4].y = vports[i].min.y; PEXPolyline2D( dpy, borders, PEXOCStore, 5, pts ); } } #define NUM_POINTS 13 #define TMAX 1000.0 #define VMAX 1.0 #define BASE_HEIGHT 0.1 static PEXCoord base_vertices[2][4] = { {{0,0,1}, {1,0,1}, {1,BASE_HEIGHT,1}, {0,BASE_HEIGHT,1}}, {{1,0,1}, {1,0,0}, {1,BASE_HEIGHT,0}, {1, BASE_HEIGHT, 1}}}; static void create_ideal_gas_surface() { PEXStructure labels; PEXCoord vertices[NUM_POINTS * (NUM_POINTS + 1)]; PEXArrayOfVertex surface_vdata; PEXArrayOfFacetData dummy; PEXCoord text_pt, offset; PEXVector2D anno_up_vec; PEXTextAlignment text_align; PEXVector scale, translate, text_dir[2]; PEXMatrix xscale, xtranslate, xform; PEXColor color; float V, dV, T, dT, maxP; int i, j, index; /* Generate the points for the surface: P = T/V. */ dV = VMAX / (NUM_POINTS - 1); dT = TMAX / (NUM_POINTS - 1); maxP = 4 * TMAX / VMAX; for ( i = 0, V = 0; i < NUM_POINTS + 1; i++, V += dV ) { for ( j = 0, T = 0; j < NUM_POINTS; j++, T += dT ) { index = i * NUM_POINTS + j; vertices[index].x = V; vertices[index].z = TMAX - T; if ( V == 0 ) { /* Clamp 0 volume to maximum pressure. */ vertices[index].y = maxP; } else if ( i == NUM_POINTS ) { /* Truncate last row to 0 pressure. */ vertices[index].x = VMAX; vertices[index].y = 0; } else { /* Compute the pressure. */ vertices[index].y = T / V; if ( vertices[index].y > maxP ) vertices[index].y = maxP; } } } /* Create the structure for the labels. */ labels = PEXCreateStructure( dpy ); /* Border the volume and temperature labels. */ for ( i = 0; i < 2; i++ ) { PEXFillArea( dpy, labels, PEXOCStore, PEXShapeConvex, True, 4, base_vertices[i] ); } /* Ensure that all text attributes are applied. */ PEXSetTextPrecision( dpy, labels, PEXOCStore, PEXStrokePrecision ); /* Create the labels. */ SET_COLOR( 1, 1, 1, color ); /* white */ PEXSetTextColor( dpy, labels, PEXOCStore, PEXColorTypeRGB, &color ); PEXSetCharHeight( dpy, labels, PEXOCStore, 0.05 ); PEXSetTextAlignment( dpy, labels, PEXOCStore, PEXHAlignCenter, PEXVAlignHalf ); text_pt.x = 0.5; text_pt.y = 0.05; text_pt.z = 1; text_dir[0].x = 1; text_dir[0].y = 0; text_dir[0].z = 0; text_dir[1].x = 0; text_dir[1].y = 1; text_dir[1].z = 0; PEXText( dpy, labels, PEXOCStore, &text_pt, &text_dir[0], &text_dir[1], strlen("Volume"), "Volume" ); text_pt.x = 1; text_pt.y = 0.05; text_pt.z = 0.5; text_dir[0].x = 0; text_dir[0].y = 0; text_dir[0].z = -1; PEXText( dpy, labels, PEXOCStore, &text_pt, &text_dir[0], &text_dir[1], strlen("Temperature"), "Temperature" ); /* Use annotation text for the "Pressure" label. */ PEXSetATextHeight( dpy, labels, PEXOCStore, 0.02 ); PEXSetATextAlignment( dpy, labels, PEXOCStore, PEXHAlignCenter, PEXVAlignHalf ); anno_up_vec.x = -1; anno_up_vec.y = 0; PEXSetATextUpVector( dpy, labels, PEXOCStore, &anno_up_vec ); text_pt.x = 0.05; text_pt.y = 0.5; text_pt.z = 0.95; offset.x = offset.y = offset.z = 0; PEXAnnotationText( dpy, labels, PEXOCStore, &text_pt, &offset, strlen("Pressure"), "Pressure" ); /* Create the structure to hold the surface. */ surface = PEXCreateStructure( dpy ); SET_COLOR( 1, 1, 1, color ); /* white */ PEXSetSurfaceColor( dpy, surface, PEXOCStore, PEXColorTypeRGB, &color ); /* Draw the labels. */ PEXExecuteStructure( dpy, surface, PEXOCStore, labels ); /* Scale the surface into a reasonable volume and center it. */ scale.x = 0.8/VMAX; scale.y = 0.8/maxP; scale.z = 0.8/TMAX; PEXScale( &scale, xscale ); translate.x = translate.y = translate.z = 0.1; PEXTranslate( &translate, xtranslate ); PEXMatrixMult( xtranslate, xscale, xform ); PEXSetLocalTransform( dpy, surface, PEXOCStore, PEXReplace, xform ); /* Create the quadrilateral mesh element for the surface. */ surface_vdata.no_data = vertices; PEXQuadrilateralMesh( dpy, surface, PEXOCStore, PEXShapeUnknown, PEXGANone, PEXGANone, PEXColorTypeRGB, dummy, NUM_POINTS, NUM_POINTS + 1, surface_vdata ); } static void redraw() { /* Redraw into the off-screen buffer. */ PEXBeginRendering( dpy, cur_buf, renderer ); { PEXExecuteStructure( dpy, renderer, PEXOCRender, model ); } PEXEndRendering( dpy, renderer, True ); XmbufDisplayBuffers( dpy, 1, &cur_buf, 0, 0 ); XFlush( dpy ); /* Make the undisplayed buffer the drawing buffer. */ cur_buf = (cur_buf == buffers[0] ? buffers[1] : buffers[0]); } static void handle_input( init_theta, init_phi ) float init_theta, init_phi; { XEvent event; int err, lastx, lasty, done = 0; float theta = init_theta, phi = init_phi; /* Read and respond to X events. */ XSelectInput( dpy, window, ButtonPressMask | Button1MotionMask | ExposureMask ); while ( !done ) { XNextEvent( dpy, &event ); switch ( event.type ) { case ButtonPress: switch ( event.xbutton.button ) { case Button1: /* Reset last X and last Y. */ lastx = event.xbutton.x; lasty = event.xbutton.y; break; default: done = 1; break; } break; case MotionNotify: /* Increment the view-plane-normal angles. */ theta += PAN_SCALE * (event.xmotion.x - lastx); phi += PAN_SCALE * (lasty - event.xmotion.y); /* Reset the view plane normal. */ vpn.x = cos( phi ) * sin( theta ); vpn.y = sin( phi ); vpn.z = cos( phi ) * cos( theta ); /* Compute the new view orientation transform. */ err = PEXViewOrientationMatrix( &vrp, &vpn, &vup, view.orientation ); /* Set the view if orientation is okay. */ if ( !err ) { PEXSetTableEntries( dpy, view_table, 1, 1, PEXLUTView, &view ); lastx = event.xmotion.x; lasty = event.xmotion.y; /* Update the display. */ redraw(); } break; case Expose: /* Flush remaining Expose events, then redraw. */ while ( XCheckTypedWindowEvent( dpy, window, Expose, &event ) ) ; /* empty statement */ redraw(); break; } } } #define WIN_GEOM "800x800+50+50" main( argc, argv ) int argc; char *argv[]; { XVisualInfo vis_info; XStandardColormap cmap_info; PEXColorApproxEntry capx_info; PEXRendererAttributes rattrs; PEXExtensionInfo *pexinfo; XColor bkgd_color; /* 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. */ bkgd_color.red = bkgd_color.green = bkgd_color.blue = 0; bkgd_color.flags = DoRed | DoGreen | DoBlue; window = ora_create_window( dpy, &vis_info, &cmap_info, WIN_GEOM, &bkgd_color ); /* Create the model and set up the PEX renderer. */ if ( STRUCTURE_SPT( pexinfo ) ) { renderer = ora_setup_renderer( dpy, window, &capx_info, &rattrs ); } else { fprintf( stderr, "Structures not supported.\n" ); exit(1); } /* Create the MBX buffers. */ if ( ora_create_mbx_buffers( dpy, window, 2, buffers ) ) /* Set the current buffer to buffer 1. */ cur_buf = buffers[1]; else exit(1); /* Initialize the view. */ view_table = rattrs.view_table; initialize_views(); /* create the surface. */ create_ideal_gas_surface(); /* Create the top-level structure. */ model = PEXCreateStructure( dpy ); /* Create and execute the borders. */ create_borders(); PEXExecuteStructure( dpy, model, PEXOCStore, borders ); /* Execute the surface once for each view. */ PEXSetViewIndex( dpy, model, PEXOCStore, 1 ); PEXExecuteStructure( dpy, model, PEXOCStore, surface ); PEXSetViewIndex( dpy, model, PEXOCStore, 2 ); PEXExecuteStructure( dpy, model, PEXOCStore, surface ); PEXSetViewIndex( dpy, model, PEXOCStore, 3 ); PEXExecuteStructure( dpy, model, PEXOCStore, surface ); PEXSetViewIndex( dpy, model, PEXOCStore, 4 ); PEXExecuteStructure( dpy, model, PEXOCStore, surface ); handle_input( (float)M_PI/4, (float)0.0706 ); XCloseDisplay( dpy ); return 0; }