Next: , Previous: Interface, Up: Modules


6.2 Example 1: Simple External Module

This section gives a very simple external module which displays an oscillating mesh. To try out this example, make a copy of the file example1.c (it is distributed with Geomview in the doc subdirectory) in your directory and compile it with the command

     cc -o example1 example1.c -lm

Then put the line

     (emodule-define "Example 1" "./example1")

in a file called .geomview in your current directory. Then invoke Geomview; it is important that you compile the example program, create the .geomview file, and invoke Geomview all in the same directory. You should see "Example 1" in the Modules browser of Geomview's Main panel; click on this entry in the browser to start the module. A surface should appear in your camera window and should begin oscillating. You can stop the module by clicking on the red "[1] Example 1" line in the Modules browser.

     
     /*
      * example1.c: oscillating mesh
      *
      * This example module is distributed with the Geomview manual.
      * If you are not reading this in the manual, see the "External
      * Modules" chapter of the manual for more details.
      *
      * This module creates an oscillating mesh.
      */
     
     #include <math.h>
     #include <stdio.h>
     
     /* F is the function that we plot
      */
     float F(x,y,t)
          float x,y,t;
     {
       float r = sqrt(x*x+y*y);
       return(sin(r + t)*sqrt(r));
     }
     
     main(argc, argv)
          char **argv;
     {
       int xdim, ydim;
       float xmin, xmax, ymin, ymax, dx, dy, t, dt;
     
       xmin = ymin = -5;             /* Set x and y            */
       xmax = ymax = 5;              /*    plot ranges         */
       xdim = ydim = 24;             /* Set x and y resolution */
       dt = 0.1;                     /* Time increment is 0.1  */
     
       /* Geomview setup.  We begin by sending the command
        *            (geometry example { : foo})
        * to Geomview.  This tells Geomview to create a geom called
        * "example" which is an instance of the handle "foo".
        */
       printf("(geometry example { : foo })\n");
       fflush(stdout);
     
       /* Loop until killed.
        */
       for (t=0; ; t+=dt) {
         UpdateMesh(xmin, xmax, ymin, ymax, xdim, ydim, t);
       }
     }
     
     /* UpdateMesh sends one mesh iteration to Geomview.  This consists of
      * a command of the form
      *    (read geometry { define foo
      *       MESH
      *       ...
      *    })
      * where ... is the actual data of the mesh.  This command tells
      * Geomview to make the value of the handle "foo" be the specified
      * mesh.
      */
     UpdateMesh(xmin, xmax, ymin, ymax, xdim, ydim, t)
          float xmin, xmax, ymin, ymax, t;
          int xdim, ydim;
     {
       int i,j;
       float x,y, dx,dy;
     
       dx = (xmax-xmin)/(xdim-1);
       dy = (ymax-ymin)/(ydim-1);
     
       printf("(read geometry { define foo \n");
       printf("MESH\n");
       printf("%1d %1d\n", xdim, ydim);
       for (j=0, y = ymin; j<ydim; ++j, y += dy) {
         for (i=0, x = xmin; i<xdim; ++i, x += dx) {
           printf("%f %f %f\t", x, y, F(x,y,t));
         }
         printf("\n");
       }
       printf("})\n");
       fflush(stdout);
     }
     

The module begins by defining a function F(x,y,t) that specifies a time-varying surface. The purpose of the module is to animate this surface over time.

The main program begins by defining some variables that specify the parameters with which the function is to be plotted.

The next bit of code in the main program prints the following line to standard output

     (geometry example { : foo })

This tells Geomview to create a geom called example which is an instance of the handle foo. Handles are a part of the OOGL file format which allow you to name a piece of geometry whose value can be specified elsewhere (and in this case updated many times); for more information on handles, See OOGL File Formats. In this case, example is the title by which the user will see the object in Geomview's object browser, and foo is the internal name of the handle that the object is a reference to.

We then do fflush(stdout) to ensure that Geomview receives this command immediately. In general, since pipes may be buffered, an external module should do this whenever it wants to be sure Geomview has actually received everything it has printed out.

The last thing in the main program is an infinite loop that cycles through calls to the procedure UpdateMesh with increasing values of t. UpdateMesh sends Geomview a command of the form

     (read geometry { define foo
     MESH
     24 24
     ...
     })

where ... is a long list of numbers. This command tells Geomview to make the value of the handle foo be the specified mesh. As soon as Geomview receives this command, the geom being displayed changes to reflect the new geometry.

The mesh is given in the format of an OOGL MESH. This begins with the keyword MESH. Next come two numbers that give the x and y dimensions of the mesh; in this case they are both 24. This line is followed by 24 lines, each containing 24 triples of numbers. Each of these triples is a point on the surface. Then finally there is a line with "})" on it that ends the "{" which began the define statement and the "(" that began the command. For more details on the format of MESH data, see MESH.

This module could be written without the use of handles by having it write out commands of the form

     (geometry example {
     MESH
     24 24
     ...
     })

This first time Geomview receives a command of this form it would create a geom called example with the given MESH data. Subsequent (geometry example ...) commands would cause Geomview to replace the geometry of the geom example with the new MESH data. If done in this way there would be no need to send the initial (geometry example { : foo }) command as above. The handle technique is useful, however, because it can be used in more general situations where a handle represents only part of a complex geom, allowing an external module to replace only that part without having to retransmit the entire geom. For more information on handles, see GCL. See References. See (hdefine ...). See (read ...).

The module loops through calls to UpdateMesh which print out commands of the above form one after the other as fast as possible. The loop continues indefinitely; the module will terminate when the user kills it by clicking on its instance line in the Modules browser, or else when Geomview exits.

Sometimes when you terminate this module by clicking on its instance entry the Modules browser, Geomview will kill it while it is in the middle of sending a command to Geomview. Geomview will then receive only a piece of a command and will print out a cryptic but harmless error message about this. When a module has a user interface panel it can use a "Quit" button to provide a more graceful way for the user to terminate the module. See the next example.

You can run this module in a shell window without Geomview to see the commands it prints out. You will have to kill it with ctrl-C to get it to stop.