/* pan_and_zoom-mb.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" #include "mbx.h" /* Globals. */ static PEXStructure model; static Display *dpy; static Window window; static PEXRenderer renderer; /* The view parameters for the view table. */ static PEXLookupTable view_table; static unsigned int view_num = 1; static Multibuffer buffers[2], cur_buf; /* The view parameters. */ static PEXCoord vrp; static PEXVector vup, vpn; static PEXCoord2D view_window[2]; static PEXNPCSubVolume viewport; static double view_plane, front_plane, back_plane; static PEXCoord prp; static int perspective; static PEXViewEntry view; /* Constants */ #define SPHERE_RADIUS 1.0 #define AXIS_LENGTH 0.4 #define PAN_SCALE (M_PI/500) #define PAN_OVER_SCALE 4.0/500 #define ZOOM_FACTOR 0.1 static void set_initial_view() { int err; /* Compute the view orientation transform. */ vrp.x = 0; vrp.y = 0; vrp.z = 0; vup.x = 0; vup.y = 1; vup.z = 0; vpn.x = 1; vpn.y = 1; vpn.z = 1; err = PEXViewOrientationMatrix( &vrp, &vpn, &vup, view.orientation ); /* The view mapping parameters. */ perspective = True; view_plane = 0; prp.x = 0; prp.y = 0; prp.z = 10; view_window[0].x = -1.5; view_window[1].x = 1.5; view_window[0].y = -1.5; view_window[1].y = 1.5; back_plane = -1.5; front_plane = 1.5; viewport.min.x = 0; viewport.max.x = 1; viewport.min.y = 0; viewport.max.y = 1; viewport.min.z = 0; viewport.max.z = 1; /* Compute the view mapping transform. */ err = PEXViewMappingMatrix( view_window, &viewport, perspective, &prp, view_plane, back_plane, front_plane, view.mapping ); /* The view clipping parameters. */ view.clip_flags = PEXClippingAll; view.clip_limits = viewport; /* Set the view. */ PEXSetTableEntries( dpy, view_table, view_num, 1, PEXLUTView, (PEXPointer) &view ); } void create_model() { PEXListOfCoord sphere_points; PEXCoord center; PEXStructure axes, sphere; /* Create the axes structure. */ center.x = center.y = center.z = 0; axes = ora_build_axes_struct( dpy, ¢er, AXIS_LENGTH ); /* Create the sphere structure. */ sphere = PEXCreateStructure( dpy ); ora_build_sphere( SPHERE_RADIUS, &sphere_points ); PEXPolyline( dpy, sphere, PEXOCStore, sphere_points.count, sphere_points.points ); /* Create the top-level structure. */ model = PEXCreateStructure( dpy ); PEXSetViewIndex( dpy, model, PEXOCStore, view_num ); PEXExecuteStructure( dpy, model, PEXOCStore, axes ); PEXExecuteStructure( dpy, model, PEXOCStore, sphere ); } 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 pan( event ) XEvent *event; { int i, j, err; PEXCoord movement, shift; static int lastx, lasty; static double theta, phi; static PEXMatrix inv_ori; /* so that it's init'ed to 0 */ switch ( event->type ) { case ButtonPress: /* Initialize. */ lastx = event->xbutton.x; lasty = event->xbutton.y; /* Compute the current view plane normal angles. */ theta = atan2( vpn.x, vpn.z ); phi = atan2( vpn.y, hypot(vpn.x, vpn.z) ); break; case MotionNotify: if ( event->xbutton.state & ShiftMask ) { /* Pan over the view plane. */ inv_ori[3][3] = 1; for ( i = 0; i < 3; i++ ) for ( j = 0; j < 3; j++ ) inv_ori[i][j] = view.orientation[j][i]; movement.x = PAN_OVER_SCALE * (event->xmotion.x - lastx); movement.y = PAN_OVER_SCALE * (lasty - event->xmotion.y); movement.z = 0; PEXTransformPoints( inv_ori, 1, &movement, &shift ); vrp.x += shift.x; vrp.y += shift.y; vrp.z += shift.z; } else { /* Pan around by moving the view plane normal. */ /* Increment the longitude and latitude. */ theta += PAN_SCALE * (event->xmotion.x - lastx); phi += PAN_SCALE * (lasty - event->xmotion.y); /* Compute the new 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, view_num, 1, PEXLUTView, &view ); lastx = event->xmotion.x; lasty = event->xmotion.y; /* Update the display. */ redraw(); } break; } } static void zoom( event ) XEvent *event; { int err; float old_width, old_height, zoom_factor; /* Compute the new view window. */ old_width = view_window[1].x - view_window[0].x; old_height = view_window[1].y - view_window[0].y; /* Divide the change evenly between sides. */ zoom_factor = 0.5 * ZOOM_FACTOR; if ( event->xbutton.state & ShiftMask ) { /* Zoom out. */ view_window[0].x -= zoom_factor * old_width; view_window[1].x += zoom_factor * old_width; view_window[0].y -= zoom_factor * old_height; view_window[1].y += zoom_factor * old_height; } else { /* Zoom in. */ view_window[0].x += zoom_factor * old_width; view_window[1].x -= zoom_factor * old_width; view_window[0].y += zoom_factor * old_height; view_window[1].y -= zoom_factor * old_height; } /* Calculate the new mapping matrix. */ err = PEXViewMappingMatrix( view_window, &viewport, False, &prp, view_plane, back_plane, front_plane, view.mapping ); /* Set the view if mapping is okay. */ if ( !err ) { PEXSetTableEntries( dpy, view_table, view_num, 1, PEXLUTView, &view ); /* Update the display. */ redraw(); } } static void handle_input() { XEvent event; int done = 0; /* 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: pan( &event ); break; case Button2: zoom( &event ); break; default: done = 1; break; } break; case MotionNotify: pan( &event ); break; case Expose: /* Flush remaining Expose events, then redraw. */ while ( XCheckTypedWindowEvent( dpy, window, Expose, &event ) ) ; /* empty statement */ redraw(); break; } } } #define WIN_GEOM "500x500+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 ) ) { create_model( dpy ); 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; set_initial_view(); handle_input(); XCloseDisplay( dpy ); return 0; }