1 /*
2  * Copyright (C) 1997-2005, R3vis Corporation.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA,
17  * or visit http://www.gnu.org/copyleft/gpl.html.
18  *
19  * Contributor(s):
20  *   Wes Bethel, R3vis Corporation, Marin County, California
21  *
22  * The OpenRM project is located at http://openrm.sourceforge.net/.
23  */
24 /*
25  * $Id: vis2d.c,v 1.13 2005/06/08 18:34:16 wes Exp $
26  * $Revision: 1.13 $
27  * $Name: OpenRM-1-6-0-2-c $
28  * $Log: vis2d.c,v $
29  * Revision 1.13  2005/06/08 18:34:16  wes
30  * Code cleanup to eliminate compiler warnings.
31  *
32  * Revision 1.12  2003/04/13 18:13:23  wes
33  * Updated copyright dates.
34  *
35  * Revision 1.11  2003/01/27 05:07:07  wes
36  * Changes to RMpipe initialization sequence APIs. Tested for GLX, but not WGL.
37  *
38  * Revision 1.10  2003/01/16 22:22:45  wes
39  * Updated all source files to reflect new organization of header files: all
40  * headers that were formerly located in include/rmaux, include/rmv and
41  * include/rmi are now located in include/rm.
42  *
43  * Revision 1.9  2002/06/17 00:46:18  wes
44  * Replaced rmSubtreeFrame with rmFrame
45  *
46  * Revision 1.8  2001/07/15 22:33:19  wes
47  * Added rmPipeDelete to the end of all demo progs. For those that use
48  * an initfunc, added a new RMnode * parm (which is unused, except for rm2screen).
49  *
50  * Revision 1.7  2001/06/03 19:44:05  wes
51  * Add calls to new rmaux routines to handle window resize events, and
52  * for keyboard event handling.
53  *
54  * Revision 1.6  2001/03/31 16:55:18  wes
55  * Added procmode.h, which defines an RMpipe processing mode used in
56  * most demonstration programs. The default processing mode is
57  * RM_PIPE_MULTISTAGE_VIEW_PARALLEL.
58  *
59  * Revision 1.5  2000/12/03 23:31:17  wes
60  * Changed default vis technique back to 0 (was 6).
61  *
62  * Revision 1.4  2000/12/02 17:24:32  wes
63  * Version 1.4.0-alpha-1 checkin. See the RELEASENOTES file for
64  * a summary of changes. With this checkin, these demo programs
65  * are no longer compatible with versions of the OpenRM API that
66  * are pre-1.4.0.
67  *
68  * Revision 1.3  2000/08/28 01:38:18  wes
69  * Updated rmaux* interfaces - rmauxEventLoop now takes two additional
70  * parameters (keypress and window resize app callbacks); replaced
71  * rmauxUI with rmauxSetGeomTransform and, where appropriate,
72  * rmauxSetCamera3DTransform.
73  *
74  * Revision 1.2  2000/04/20 18:04:34  wes
75  * Minor tweaks and reorg for OpenRM 1.2.1.
76  *
77  * Revision 1.1.1.1  2000/02/28 21:55:30  wes
78  * OpenRM 1.2 Release
79  *
80  * Revision 1.10  2000/02/28 17:21:56  wes
81  * RM 1.2, pre-OpenRM
82  *
83  */
84 
85 #include <rm/rm.h>
86 #include <rm/rmaux.h>
87 #include <rm/rmv.h>
88 #include <rm/rmi.h>
89 #include "libdio.h"
90 #include "procmode.h"
91 
92 /*
93  * usage: vis2d -i datafilename [-w img_width] [-h img_height] \
94  *     [-v vis_technique (0..8)] [-c (use default vis colormap)]
95  */
96 
97 static RMpipe *lone_pipe=NULL;
98 static char MyRootName[]={"MyRoot"};
99 static RMnode *MyRoot;
100 int img_width=400,img_height=300;
101 char datafilename[256]={"data/cos.dio"};
102 dioDataObject *mydataobj=NULL;
103 int do_color=0;
104 int vis_technique=0;
105 RMenum my_linewidth = RM_LINEWIDTH_MEDIUM;
106 RMenum my_linestyle = RM_LINES_SOLID;
107 
108 void
usage(char * av[])109 usage(char *av[])
110 {
111     char buf[256];
112 
113     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]);
114 
115     rmError(buf);
116 }
117 
118 void
parse_args(int ac,char * av[])119 parse_args(int ac,
120 	   char *av[])
121 {
122     int i;
123 
124     i = 1;
125     while (i < ac)
126     {
127 	if (strcmp(av[i],"-w") == 0)
128 	{
129 	    i++;
130 	    sscanf(av[i],"%d",&img_width);
131 	}
132 	else if (strcmp(av[i],"-h") == 0)
133         {
134 	    i++;
135 	    sscanf(av[i],"%d",&img_height);
136 	}
137 	else if (strcmp(av[i],"-i") == 0)
138 	{
139 	    i++;
140 	    strcpy(datafilename,av[i]);
141 	}
142 	else if (strcmp(av[i],"-c") == 0)
143 	{
144 	    do_color=1;
145 	}
146 	else if (strcmp(av[i],"-v") == 0)
147 	{
148 	    i++;
149 	    sscanf(av[i],"%d",&vis_technique);
150 	    if (vis_technique < 0 || vis_technique > 8)
151 	    {
152 	        usage(av);
153 		exit(-1);
154 	    }
155 	}
156 	else
157 	{
158 	    usage(av);
159 	    exit(-1);
160 	}
161 	i++;
162     }
163 }
164 
165 
166 void
my_read_data(char * datafilename)167 my_read_data(char *datafilename)
168 {
169     mydataobj = dioReadDataObject(datafilename);
170     if (mydataobj == NULL)
171     {
172 	fprintf(stderr," error reading input data file. exiting. \n");
173 	exit(-1);
174     }
175     dioObjectConditioner(mydataobj);
176 }
177 
178 void
my_set_scene(RMnode * camNode)179 my_set_scene(RMnode *camNode)
180 {
181 
182     RMcamera2D *c=rmCamera2DNew();
183     float vp[4] = {0.05F,0.05F,0.95F,0.95F};
184 
185     /* create a camera */
186     rmDefaultCamera2D(c);		/* assign it some default values. */
187 
188     /* adjust default view so all geom is visible */
189     rmCamera2DComputeViewFromGeometry(c,MyRoot);
190 
191     {
192 	/*
193 	 * modify the 2d camera so that it's extents are 5% larger than
194 	 * the actual geometry so that stuff on the edges doesn't get
195 	 * clipped away.
196 	 */
197 	float xmin,ymin,xmax,ymax,dx,dy;
198 
199 	rmCamera2DGetExtents(c,&xmin,&ymin,&xmax,&ymax);
200 	dx = xmax - xmin;
201 	dy = ymax - ymin;
202 	xmin -= 0.05*dx;
203 	xmax += 0.05*dx;
204 	ymin -= 0.05*dy;
205 	ymax += 0.05*dy;
206 
207 	rmCamera2DSetExtents(c,xmin,ymin,xmax,ymax);
208     }
209 
210 
211     /* add the camera to "my root's" scene parms. */
212     rmNodeSetSceneCamera2D(camNode,c);
213 
214     /* set the viewport to be a little smaller than the window so that
215      the plot doesn't abut to the window edges.*/
216     rmNodeSetSceneViewport(camNode,vp);
217 
218     rmCamera2DDelete(c);
219 }
220 
221 
222 /*
223  * the following two routines are the interface between the RMV
224  * vis tools and the local data model. RMV wants us to supply routines
225  * which will tell the vis tool what the (x,y) point is at some grid
226  * location, and what the data point is at some grid location.
227  *
228  * the local data model is very simple, so we can make simplifying
229  * assumptions resulting in very terse routines.
230  */
231 RMvertex2D
mygridfunc(int i)232 mygridfunc(int i)
233 {
234     /*
235      * tell RMV what this grid (x,y) point is at location "i". we assume
236      * a one-d grid of (x,y) points in the local data model.
237      */
238     RMvertex2D temp2d;
239 
240     temp2d.x = mydataobj->xcoords[i];
241     temp2d.y = mydataobj->ycoords[i];
242     return(temp2d);
243 }
244 
245 float
mydatafunc(int i)246 mydatafunc(int i)
247 {
248     /*
249      * tell RMV what the data value is at grid location "i". we
250      * assume a one-d grid.
251      */
252     return(mydataobj->rawdata[i]);
253 }
254 
255 void
my_build_objs(void)256 my_build_objs(void)
257 {
258     MyRoot = rmNodeNew(MyRootName,RM_RENDERPASS_2D, RM_RENDERPASS_ALL);
259     rmNodeAddChild(rmRootNode(),MyRoot);
260 
261     /* do the visualization.. */
262     {
263 	RMnode *visnode;
264 	int offset_flag;
265 	float *xcoords,*ycoords,*data;
266 	int npts;
267 	float zeroval=0.0;
268 	float shrink = 0.8;
269 	RMvisMap *vmap=NULL;
270 
271 	if (mydataobj->width != 1)
272 	{
273 	    offset_flag = RMV_YAXIS_OFFSET;
274 	    npts = mydataobj->width;
275 	}
276 	else
277 	{
278 	    offset_flag = RMV_XAXIS_OFFSET;
279 	    npts = mydataobj->height;
280 	}
281 
282 	if (do_color)
283 	{
284 	    vmap = rmDefaultVismap();
285 
286 	    rmVismapSetTfMin(vmap,mydataobj->datamin);
287 	    rmVismapSetTfMax(vmap,mydataobj->datamax);
288 	}
289 
290 	visnode = rmNodeNew("vis",RM_RENDERPASS_2D, RM_RENDERPASS_ALL);
291 
292 	xcoords = mydataobj->xcoords;
293 	ycoords = mydataobj->ycoords;
294 	data = mydataobj->rawdata;
295 
296 
297 	switch(vis_technique)
298 	{
299 	case 0:
300 	    shrink = 1.0F;
301 	    rmvI2BarFilled(mygridfunc,
302 			   mydatafunc,
303 			   (vmap == NULL) ? NULL : mydatafunc,
304 			   vmap,
305 			   offset_flag,
306 			   npts,
307 			   shrink,
308 			   RMV_SCALE_RELATIVE,
309 			   visnode);
310 	    break;
311 
312 	case 1:
313 	    shrink = 0.5F;
314 	    my_linewidth = RM_LINEWIDTH_NARROW;
315 	    rmvI2BarOutline(mygridfunc,
316 			    mydatafunc,
317 			    (vmap == NULL) ? NULL : mydatafunc,
318 			    vmap,
319 			    offset_flag,
320 			    npts,
321 			    shrink,
322 			    RMV_SCALE_RELATIVE,
323 			    my_linewidth,
324 			    my_linestyle,
325 			    visnode);
326 	    break;
327 
328 	case 2:
329 	    my_linestyle = RM_LINES_DASHED;
330 	    my_linewidth = RM_LINEWIDTH_HEAVY;
331 	    rmvI2Polyline(mygridfunc,
332 			  mydatafunc,
333 			  (vmap == NULL) ? NULL : mydatafunc,
334 			  vmap,
335 			  offset_flag,
336 			  npts,
337 			  my_linewidth,my_linestyle,
338 			  visnode);
339 	    break;
340 
341 	case 3:
342 	    rmvI2Impulse(mygridfunc,
343 			 mydatafunc,
344 			 (vmap == NULL) ? NULL : mydatafunc,
345 			 vmap,
346 			 offset_flag,
347 			 npts,
348 			 my_linewidth,my_linestyle,
349 			 visnode);
350 	    break;
351 
352 	case 4:
353 
354 	    zeroval = 0.5;
355 
356 	    rmvI2AreaFill(mygridfunc,
357 			  mydatafunc,
358 			  (vmap == NULL) ? NULL : mydatafunc,
359 			  vmap,
360 			  offset_flag,
361 			  npts,
362 			  zeroval,
363 			  visnode);
364 	    break;
365 
366 	case 5:
367 	    {
368 		int marker_enum = RMV_2DMARKER_NORTHTRIANGLE_FILLED;
369 /*		int scaling_flag = RMV_SCALE_ABSOLUTE; experiment with me! */
370 		int scaling_flag = RMV_SCALE_RELATIVE;
371 
372 		shrink = 2.0;
373 
374 		rmvI2ScatterGeom(mygridfunc,
375 				 mydatafunc,
376 				 (vmap == NULL) ? NULL : mydatafunc,
377 				 vmap,
378 				 offset_flag,
379 				 npts,
380 				 shrink,
381 				 scaling_flag,
382 				 marker_enum,
383 				 visnode);
384 	    }
385 	    break;
386 
387 	case 6:
388 	    {
389 		/* experiment with us! */
390 		int marker_enum = RMV_ZAPFMARKER_STAR_FILLED;
391 		int size_enum = RM_FONT_S;
392 
393 		rmvI2ScatterGlyph(mygridfunc,
394 				  mydatafunc,
395 				  (vmap == NULL) ? NULL : mydatafunc,
396 				  vmap,
397 				  offset_flag,
398 				  npts,
399 				  size_enum,
400 				  marker_enum,
401 				  visnode);
402 
403 	    }
404 	    break;
405 
406 	case 7:
407 	{
408 	    rmvI2Impulse(mygridfunc,
409 			 mydatafunc,
410 			 (vmap == NULL) ? NULL : mydatafunc,
411 			 vmap,
412 			 offset_flag,
413 			 npts,
414 			 my_linewidth,my_linestyle,
415 			 visnode);
416 	    rmvI2Step(mygridfunc,
417 		      mydatafunc,
418 		      (vmap == NULL) ? NULL : mydatafunc,
419 		      vmap,
420 		      offset_flag,
421 		      npts,
422 		      zeroval,
423 		      my_linewidth,
424 		      my_linestyle,
425 		      visnode);
426 	}
427 	    break;
428 
429 	case 8:
430 	{
431 	    rmvI2AreaOutline(mygridfunc,
432 		      mydatafunc,
433 		      (vmap == NULL) ? NULL : mydatafunc,
434 		      vmap,
435 		      offset_flag,
436 		      npts,
437 		      zeroval,
438 		      my_linewidth,
439 		      my_linestyle,
440 		      visnode);
441 	    break;
442 	}
443 
444 
445 	default:
446 	    break;
447 	}
448 
449 	rmNodeAddChild(MyRoot,visnode);
450 	rmNodeComputeBoundingBox(visnode);
451 
452 	rmNodeUnionAllBoxes(MyRoot);
453 
454 	if (vmap != NULL)
455 	    rmVismapDelete(vmap);
456     }
457 
458 }
459 
460 
461 void
dumpimagefunc(const RMimage * img,RMenum whichbufferEnum)462 dumpimagefunc(const RMimage *img,
463 	      RMenum whichbufferEnum)
464 {
465     char fname[] = "vis2d.jpg";
466     char buf[64];
467 
468     sprintf(buf," writing an image file named <%s> ",fname);
469     rmNotice(buf);
470 
471     rmiWriteJPEG(fname, 99, img);
472 
473     /* foil compiler warning */
474     whichbufferEnum = RM_TRUE;
475 }
476 
477 int
my_dump_image_func(RMpipe * p,int xbutton,int ybutton)478 my_dump_image_func(RMpipe *p,
479 		   int xbutton,
480 		   int ybutton)
481 {
482     /*
483      * the goal is to write an image file that contains the
484      * contents of the framebuffer.
485      */
486     rmNotice(" in my_dump_image_func(). \n");
487 
488     /*
489      * assign a "post render" function - it will be invoked after
490      * the scene has been rendered, and will write the framebuffer
491      * contents to a file.
492      */
493     rmPipeSetPostRenderFunc(p,dumpimagefunc);
494 
495     /*
496      * render & write the image.
497      */
498     rmFrame(p, rmRootNode());
499 
500     /*
501      * now, remove the "write image" function from the pipe. if we
502      * didn't remove this callback, we'd write a file ever time the
503      * frame is rendered - which is useful, but not what we want
504      * in this context.
505      */
506     rmPipeSetPostRenderFunc(lone_pipe,NULL);
507 
508     /* foil compiler warning */
509     xbutton = ybutton = 0;
510 
511     return(1);
512 }
513 
514 void
myinitfunc(RMpipe * p,RMnode * n)515 myinitfunc(RMpipe *p,
516 	   RMnode *n)
517 {
518     my_read_data(datafilename);
519     my_build_objs();
520     my_set_scene(MyRoot);
521 
522     /*
523      * restrict rendering in this app to only 2D Opaque objects.
524      * we'll set the background color at the rmRootNode(), since we
525      * tweak the viewport for 2D rendering in MyRoot.
526      */
527     {
528 	RMcolor4D bgcolor={0.2,0.2,0.3,1.0};
529 	rmPipeSetRenderPassEnable(p, RM_FALSE, RM_FALSE, RM_TRUE);
530 	rmPipeSetSceneBackgroundColor(p, &bgcolor);
531     }
532 
533     rmauxSetGeomTransform(MyRoot,p);
534 
535     rmauxSetButtonDownFunc(RM_BUTTON1,RM_CONTROL_MODMASK,my_dump_image_func);
536     /*
537      * set handler to reset aspect ratio when the window is resized.
538      */
539     rmauxSetResizeFunc(p, MyRoot, rmauxDefaultResizeFunc);
540 
541     if (rmPipeProcessingModeIsMultithreaded(p) == RM_TRUE)
542 	rmFrame(p, n);
543 
544     rmFrame(p, n);
545 }
546 
547 
548 #ifdef RM_WIN
WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpszCmdLine,int nCmdShow)549 int WINAPI WinMain (HINSTANCE hInstance,
550 		    HINSTANCE hPrevInstance,
551                     LPSTR lpszCmdLine, int nCmdShow)
552 {
553     MSG      msg;
554     HWND     hWnd;
555     void *fptr;
556     RMenum processingMode = DEFAULT_PROCESSING_MODE; /* in procmode.h */
557     RMenum targetPlatform = RM_PIPE_WGL;
558 
559     parse_args(__argc,__argv);
560 #else  /* assume RM_X */
561 int
562 main(int ac,
563      char *av[])
564 {
565     RMenum processingMode = DEFAULT_PROCESSING_MODE; /* in procmode.h */
566     RMenum targetPlatform = RM_PIPE_GLX;
567     void *msg;			/* needed for rmauxEventLoop
568 				   win32/unix API consistency */
569 
570     parse_args(ac,av);
571 #endif
572 
573     /*
574      * first stage of RM initialization.
575      */
576     rmInit();
577 
578     /*
579      * create the rendering pipe. this step is required in both
580      * Win32 and X.
581      */
582 
583     lone_pipe = rmPipeNew(targetPlatform);
584     rmPipeSetProcessingMode(lone_pipe, processingMode);
585 
586 #ifdef RM_WIN
587     {
588         /*
589 	 * Win32: when a window is created, we have to tell windows the
590 	 * name of the "WndProc," the procedure that gets called by
591 	 * windows with events (the event loop) (contrast to the X model
592 	 * where the name of the event loop is not part of the window).
593 	 * Since we're using RMaux, we know about the event handling
594 	 * procedure named "rmauxWndProc" and we provide that here.
595 	 */
596 
597         fptr = (void *)(rmauxWndProc);
598 	hWnd = rmauxCreateW32Window(lone_pipe,
599 			       NULL, /* no parent window */
600 			       20,20,img_width,img_height,"RM for Windows",
601 			       hInstance,fptr);
602 	if (hWnd == 0)
603 	  exit(-1);
604 
605 	/*
606 	 * assign the new window handle to the rendering pipe.
607 	 */
608 	rmPipeSetWindow(lone_pipe,hWnd, img_width, img_height);
609     }
610 
611 #endif
612 #ifdef RM_X
613     {
614 	Window w;
615 
616 	w = rmauxCreateXWindow(lone_pipe,
617 			       (Window)NULL, /* parent window */
618 			       0,0,img_width,img_height,
619 			       "RM for X-Windows","icon-title",RM_TRUE);
620 	/*
621 	 * assign the window to the rendering pipe.
622 	 */
623 	rmPipeSetWindow(lone_pipe,w,img_width,img_height);
624     }
625 #endif
626 
627     /*
628      * specify the name of the "init" function. the "init" function is
629      * mandatory in the Win32 world, and optional in the X world.
630      *
631      * in Win32, we don't want to call RM services until OpenGL is
632      * ready. we can be assured of readiness by using an init function
633      * with RMaux.
634      *
635      * in X, at this point, the window is mapped and OpenGL is ready,
636      * and we could call our init function directly.
637      */
638 
639     rmauxSetInitFunc(myinitfunc);
640 
641     /*
642      * X-ism: once the window is created and assigned to the
643      * rendering pipe, rmPipeMakeCurrent makes the OpenGL rendering context
644      * current for the pipe+window combination.
645      *
646      * this step is required for X. in these demo programs, it is not
647      * strictly required by Win32, as the newly created context is made
648      * current as part of the OpenGL initialization sequence.
649      */
650     rmPipeMakeCurrent(lone_pipe);
651 
652     /*
653      * set key handler function so this prog will exit on "q" key.
654      */
655     rmauxSetKeyFunc(lone_pipe, rmauxDefaultKeyFunc);
656 
657     rmauxEventLoop(lone_pipe,rmRootNode(), &msg);
658 
659     rmPipeDelete(lone_pipe);
660     rmFinish();
661 
662 #ifdef RM_WIN
663     return( msg.wParam );
664 #else
665     return(1);
666 #endif
667 }
668