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