Next: Example2, 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 "[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.