/* * Copyright (C) 1997-2003, R3vis Corporation. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA, * or visit http://www.gnu.org/copyleft/gpl.html. * * Contributor(s): * Wes Bethel, R3vis Corporation, Marin County, California * * The OpenRM project is located at http://openrm.sourceforge.net/. */ /* * $Id: vis2d.c,v 1.12 2003/04/13 18:13:23 wes Exp $ * $Revision: 1.12 $ * $Name: OpenRM-1-5-2-RC1 $ * $Log: vis2d.c,v $ * Revision 1.12 2003/04/13 18:13:23 wes * Updated copyright dates. * * Revision 1.11 2003/01/27 05:07:07 wes * Changes to RMpipe initialization sequence APIs. Tested for GLX, but not WGL. * * Revision 1.10 2003/01/16 22:22:45 wes * Updated all source files to reflect new organization of header files: all * headers that were formerly located in include/rmaux, include/rmv and * include/rmi are now located in include/rm. * * Revision 1.9 2002/06/17 00:46:18 wes * Replaced rmSubtreeFrame with rmFrame * * Revision 1.8 2001/07/15 22:33:19 wes * Added rmPipeDelete to the end of all demo progs. For those that use * an initfunc, added a new RMnode * parm (which is unused, except for rm2screen). * * Revision 1.7 2001/06/03 19:44:05 wes * Add calls to new rmaux routines to handle window resize events, and * for keyboard event handling. * * Revision 1.6 2001/03/31 16:55:18 wes * Added procmode.h, which defines an RMpipe processing mode used in * most demonstration programs. The default processing mode is * RM_PIPE_MULTISTAGE_VIEW_PARALLEL. * * Revision 1.5 2000/12/03 23:31:17 wes * Changed default vis technique back to 0 (was 6). * * Revision 1.4 2000/12/02 17:24:32 wes * Version 1.4.0-alpha-1 checkin. See the RELEASENOTES file for * a summary of changes. With this checkin, these demo programs * are no longer compatible with versions of the OpenRM API that * are pre-1.4.0. * * Revision 1.3 2000/08/28 01:38:18 wes * Updated rmaux* interfaces - rmauxEventLoop now takes two additional * parameters (keypress and window resize app callbacks); replaced * rmauxUI with rmauxSetGeomTransform and, where appropriate, * rmauxSetCamera3DTransform. * * Revision 1.2 2000/04/20 18:04:34 wes * Minor tweaks and reorg for OpenRM 1.2.1. * * Revision 1.1.1.1 2000/02/28 21:55:30 wes * OpenRM 1.2 Release * * Revision 1.10 2000/02/28 17:21:56 wes * RM 1.2, pre-OpenRM * */ #include <rm/rm.h> #include <rm/rmaux.h> #include <rm/rmv.h> #include "libdio.h" #include "procmode.h" /* * usage: vis2d -i datafilename [-w img_width] [-h img_height] \ * [-v vis_technique (0..8)] [-c (use default vis colormap)] */ static RMpipe *lone_pipe=NULL; static char MyRootName[]={"MyRoot"}; static RMnode *MyRoot; int img_width=400,img_height=300; char datafilename[256]={"data/cos.dio"}; dioDataObject *mydataobj=NULL; int do_color=0; int vis_technique=0; RMenum my_linewidth = RM_LINEWIDTH_MEDIUM; RMenum my_linestyle = RM_LINES_SOLID; void usage(char *av[]) { char buf[256]; sprintf(buf," usage: %s [-i datafilename (defaults to data/cos.dio) [-w img_width] [-h img_height] [-c (use default vis colormap to 'colorize' the plot') [-v n (where n=0..8, and indicates which visualization technique to use)]\n",av[0]); rmError(buf); } void parse_args(int ac, char *av[]) { int i; i = 1; while (i < ac) { if (strcmp(av[i],"-w") == 0) { i++; sscanf(av[i],"%d",&img_width); } else if (strcmp(av[i],"-h") == 0) { i++; sscanf(av[i],"%d",&img_height); } else if (strcmp(av[i],"-i") == 0) { i++; strcpy(datafilename,av[i]); } else if (strcmp(av[i],"-c") == 0) { do_color=1; } else if (strcmp(av[i],"-v") == 0) { i++; sscanf(av[i],"%d",&vis_technique); if (vis_technique < 0 || vis_technique > 8) { usage(av); exit(-1); } } else { usage(av); exit(-1); } i++; } } void my_read_data(char *datafilename) { mydataobj = dioReadDataObject(datafilename); if (mydataobj == NULL) { fprintf(stderr," error reading input data file. exiting. \n"); exit(-1); } dioObjectConditioner(mydataobj); } void my_set_scene(RMnode *camNode) { RMcamera2D *c=rmCamera2DNew(); float vp[4] = {0.05F,0.05F,0.95F,0.95F}; /* create a camera */ rmDefaultCamera2D(c); /* assign it some default values. */ /* adjust default view so all geom is visible */ rmCamera2DComputeViewFromGeometry(c,MyRoot); { /* * modify the 2d camera so that it's extents are 5% larger than * the actual geometry so that stuff on the edges doesn't get * clipped away. */ float xmin,ymin,xmax,ymax,dx,dy; rmCamera2DGetExtents(c,&xmin,&ymin,&xmax,&ymax); dx = xmax - xmin; dy = ymax - ymin; xmin -= 0.05*dx; xmax += 0.05*dx; ymin -= 0.05*dy; ymax += 0.05*dy; rmCamera2DSetExtents(c,xmin,ymin,xmax,ymax); } /* add the camera to "my root's" scene parms. */ rmNodeSetSceneCamera2D(camNode,c); /* set the viewport to be a little smaller than the window so that the plot doesn't abut to the window edges.*/ rmNodeSetSceneViewport(camNode,vp); rmCamera2DDelete(c); } /* * the following two routines are the interface between the RMV * vis tools and the local data model. RMV wants us to supply routines * which will tell the vis tool what the (x,y) point is at some grid * location, and what the data point is at some grid location. * * the local data model is very simple, so we can make simplifying * assumptions resulting in very terse routines. */ RMvertex2D mygridfunc(int i) { /* * tell RMV what this grid (x,y) point is at location "i". we assume * a one-d grid of (x,y) points in the local data model. */ RMvertex2D temp2d; temp2d.x = mydataobj->xcoords[i]; temp2d.y = mydataobj->ycoords[i]; return(temp2d); } float mydatafunc(int i) { /* * tell RMV what the data value is at grid location "i". we * assume a one-d grid. */ return(mydataobj->rawdata[i]); } void my_build_objs(void) { MyRoot = rmNodeNew(MyRootName,RM_RENDERPASS_2D, RM_RENDERPASS_ALL); rmNodeAddChild(rmRootNode(),MyRoot); /* do the visualization.. */ { RMnode *visnode; int offset_flag; float *xcoords,*ycoords,*data; int npts; float zeroval=0.0; float shrink = 0.8; RMvisMap *vmap=NULL; if (mydataobj->width != 1) { offset_flag = RMV_YAXIS_OFFSET; npts = mydataobj->width; } else { offset_flag = RMV_XAXIS_OFFSET; npts = mydataobj->height; } if (do_color) { vmap = rmDefaultVismap(); rmVismapSetTfMin(vmap,mydataobj->datamin); rmVismapSetTfMax(vmap,mydataobj->datamax); } visnode = rmNodeNew("vis",RM_RENDERPASS_2D, RM_RENDERPASS_ALL); xcoords = mydataobj->xcoords; ycoords = mydataobj->ycoords; data = mydataobj->rawdata; switch(vis_technique) { case 0: shrink = 1.0F; rmvI2BarFilled(mygridfunc, mydatafunc, (vmap == NULL) ? NULL : mydatafunc, vmap, offset_flag, npts, shrink, RMV_SCALE_RELATIVE, visnode); break; case 1: shrink = 0.5F; my_linewidth = RM_LINEWIDTH_NARROW; rmvI2BarOutline(mygridfunc, mydatafunc, (vmap == NULL) ? NULL : mydatafunc, vmap, offset_flag, npts, shrink, RMV_SCALE_RELATIVE, my_linewidth, my_linestyle, visnode); break; case 2: my_linestyle = RM_LINES_DASHED; my_linewidth = RM_LINEWIDTH_HEAVY; rmvI2Polyline(mygridfunc, mydatafunc, (vmap == NULL) ? NULL : mydatafunc, vmap, offset_flag, npts, my_linewidth,my_linestyle, visnode); break; case 3: rmvI2Impulse(mygridfunc, mydatafunc, (vmap == NULL) ? NULL : mydatafunc, vmap, offset_flag, npts, my_linewidth,my_linestyle, visnode); break; case 4: zeroval = 0.5; rmvI2AreaFill(mygridfunc, mydatafunc, (vmap == NULL) ? NULL : mydatafunc, vmap, offset_flag, npts, zeroval, visnode); break; case 5: { int marker_enum = RMV_2DMARKER_NORTHTRIANGLE_FILLED; /* int scaling_flag = RMV_SCALE_ABSOLUTE; experiment with me! */ int scaling_flag = RMV_SCALE_RELATIVE; shrink = 2.0; rmvI2ScatterGeom(mygridfunc, mydatafunc, (vmap == NULL) ? NULL : mydatafunc, vmap, offset_flag, npts, shrink, scaling_flag, marker_enum, visnode); } break; case 6: { /* experiment with us! */ int marker_enum = RMV_ZAPFMARKER_STAR_FILLED; int size_enum = RM_FONT_S; rmvI2ScatterGlyph(mygridfunc, mydatafunc, (vmap == NULL) ? NULL : mydatafunc, vmap, offset_flag, npts, size_enum, marker_enum, visnode); } break; case 7: { rmvI2Impulse(mygridfunc, mydatafunc, (vmap == NULL) ? NULL : mydatafunc, vmap, offset_flag, npts, my_linewidth,my_linestyle, visnode); rmvI2Step(mygridfunc, mydatafunc, (vmap == NULL) ? NULL : mydatafunc, vmap, offset_flag, npts, zeroval, my_linewidth, my_linestyle, visnode); } break; case 8: { rmvI2AreaOutline(mygridfunc, mydatafunc, (vmap == NULL) ? NULL : mydatafunc, vmap, offset_flag, npts, zeroval, my_linewidth, my_linestyle, visnode); break; } default: break; } rmNodeAddChild(MyRoot,visnode); rmNodeComputeBoundingBox(visnode); rmNodeUnionAllBoxes(MyRoot); if (vmap != NULL) rmVismapDelete(vmap); } } void dumpimagefunc(const RMimage *img, RMenum whichbufferEnum) { char fname[] = "vis2d.x"; char buf[64]; sprintf(buf," writing an image file named <%s> ",fname); rmNotice(buf); dioWriteAVSImage(img,fname); } int my_dump_image_func(RMpipe *p, int xbutton, int ybutton) { /* * the goal is to write an image file that contains the * contents of the framebuffer. */ rmNotice(" in my_dump_image_func(). \n"); /* * assign a "post render" function - it will be invoked after * the scene has been rendered, and will write the framebuffer * contents to a file. */ rmPipeSetPostRenderFunc(p,dumpimagefunc); /* * render & write the image. */ rmFrame(p, rmRootNode()); /* * now, remove the "write image" function from the pipe. if we * didn't remove this callback, we'd write a file ever time the * frame is rendered - which is useful, but not what we want * in this context. */ rmPipeSetPostRenderFunc(lone_pipe,NULL); return(1); } void myinitfunc(RMpipe *p, RMnode *n) { my_read_data(datafilename); my_build_objs(); my_set_scene(MyRoot); /* * restrict rendering in this app to only 2D Opaque objects. * we'll set the background color at the rmRootNode(), since we * tweak the viewport for 2D rendering in MyRoot. */ { RMcolor4D bgcolor={0.2,0.2,0.3,1.0}; rmPipeSetRenderPassEnable(p, RM_FALSE, RM_FALSE, RM_TRUE); rmNodeSetSceneBackgroundColor(rmRootNode(), &bgcolor); } rmauxSetGeomTransform(MyRoot,p); rmauxSetButtonDownFunc(RM_BUTTON1,RM_CONTROL_MODMASK,my_dump_image_func); /* * set handler to reset aspect ratio when the window is resized. */ rmauxSetResizeFunc(p, MyRoot, rmauxDefaultResizeFunc); if (rmPipeProcessingModeIsMultithreaded(p) == RM_TRUE) rmFrame(p, rmRootNode()); rmFrame(p, rmRootNode()); } #ifdef RM_WIN int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) { MSG msg; HWND hWnd; void *fptr; RMenum processingMode = DEFAULT_PROCESSING_MODE; /* in procmode.h */ RMenum targetPlatform = RM_PIPE_WGL; int status; parse_args(__argc,__argv); #else /* assume RM_X */ int main(int ac, char *av[]) { int status; RMenum processingMode = DEFAULT_PROCESSING_MODE; /* in procmode.h */ RMenum targetPlatform = RM_PIPE_GLX; void *msg; /* needed for rmauxEventLoop win32/unix API consistency */ parse_args(ac,av); #endif /* * first stage of RM initialization. */ rmInit(); /* * create the rendering pipe. this step is required in both * Win32 and X. */ lone_pipe = rmPipeNew(targetPlatform); rmPipeSetProcessingMode(lone_pipe, processingMode); #ifdef RM_WIN { /* * Win32: when a window is created, we have to tell windows the * name of the "WndProc," the procedure that gets called by * windows with events (the event loop) (contrast to the X model * where the name of the event loop is not part of the window). * Since we're using RMaux, we know about the event handling * procedure named "rmauxWndProc" and we provide that here. */ fptr = (void *)(rmauxWndProc); hWnd = rmauxCreateW32Window(lone_pipe, NULL, /* no parent window */ 20,20,img_width,img_height,"RM for Windows", hInstance,fptr); if (hWnd == 0) exit(-1); /* * assign the new window handle to the rendering pipe. */ rmPipeSetWindow(lone_pipe,hWnd, img_width, img_height); } #endif #ifdef RM_X { Window w; w = rmauxCreateXWindow(lone_pipe, (Window)NULL, /* parent window */ 0,0,img_width,img_height, "RM for X-Windows","icon-title",RM_TRUE); /* * assign the window to the rendering pipe. */ rmPipeSetWindow(lone_pipe,w,img_width,img_height); } #endif /* * specify the name of the "init" function. the "init" function is * mandatory in the Win32 world, and optional in the X world. * * in Win32, we don't want to call RM services until OpenGL is * ready. we can be assured of readiness by using an init function * with RMaux. * * in X, at this point, the window is mapped and OpenGL is ready, * and we could call our init function directly. */ rmauxSetInitFunc(myinitfunc); /* * X-ism: once the window is created and assigned to the * rendering pipe, rmPipeMakeCurrent makes the OpenGL rendering context * current for the pipe+window combination. * * this step is required for X. in these demo programs, it is not * strictly required by Win32, as the newly created context is made * current as part of the OpenGL initialization sequence. */ rmPipeMakeCurrent(lone_pipe); /* * set key handler function so this prog will exit on "q" key. */ rmauxSetKeyFunc(lone_pipe, rmauxDefaultKeyFunc); rmauxEventLoop(lone_pipe,rmRootNode(), &msg); rmPipeDelete(lone_pipe); rmFinish(); #ifdef RM_WIN return( msg.wParam ); #else return(1); #endif }