next up previous contents index
Next: Project Curve Up: Project Time-Object Previous: Time and Variable Rulers

A Time-Object Example

 

The format of the Time-Object description files is very simple, it just contains task, frame and parameter information. All description files of subclasses of Time_Object contain this data, to parse it the function g_time_parm_get_data (see 7.4.1.2) is provided.

#
# torus_knot.to: test object for time_object - (m, n) torus knot
#

# task
GEOMETRY;

# frame data
0; 3;           # frame / number of frames

# parameter data
2; 2;           # number of int / double parameter

# int parameter
8;              # m
3;              # n

# double parameter
2.5;            # radius
1.0;            # torus radius

As you can see each number or string has to be terminated by a ; and comments start with #. The task always has to be GEOMETRY for Time_Object files, subclasses define more geometries and allow therefore more tasks. The number of the current frame always must be in the interval [0, number of frames - 1] (C convention, only the frame button of the user-interface shifts the frame number).

This torus_knot.to file can be found in the demo directory of the GRAPE distribution, in the same directory there are two spline files for the double variables called torus_knot_parm_0.sp and torus_knot_parm_1.sp, they are loaded automatically when the torus_knot.to file is read in using the tex2html_wrap44662 button (look at the message printed in the shell).

After reading in the file an (8, 3) torus knot should appear in the graphics window (perhaps you should also read the torus_knot.st status file using the tex2html_wrap45052 button in the manag menu). If you change the time or the frame the torus knot radii should vary -- unless the spline files were missing, in this case you first have to edit a spline to see a difference between the three frames.

There is no data for a torus knot in the description files or the splines, so how is the torus knot computed? Since the geometry of a Time_Object is arbitrary (in this case Triang1d) a "compute-send" method has to be provided to create the data:

TIME_OBJECT *time_object_compute_send()
{
  TIME_OBJECT *self;

  self = (TIME_OBJECT *)START_METHOD(G_INSTANCE);
  ASSURE(self, "", END_METHOD(NULL));

  /* no frames -> nothing to compute */
  if(!self->num_of_frames)
    END_METHOD(NULL);

  /* different objects are distinguished by their name (same as filename) */
  if(!strcmp(self->name, "torus_knot")) {
    TIMESTEP *step;
    TRIANG1D *triang;
    int i, m, n, num_points;
    double r, rad, torad, delta;
    int ppr = 30, ggT;

    /* copy the variables so we can use the shorter names */
    m = self->time_parm->int_parm[0];
    n = self->time_parm->int_parm[1];
    rad = self->time_parm->double_parm[0];
    torad = self->time_parm->double_parm[1];

    /*  divide out gcd to avoid identical segments */
    ggT = g_gcd(m, n);
    num_points = ppr * (m + n) / ggT;

    /* create the geometry with "alloc" and "create-objects" */
    if(!self->geometry)
      self->geometry = (CHAIN *)GRAPE(Chain, "new-instance")("geom");
    if(!self->geometry->object) {
      self->geometry->object = (INSTANCE *)
        GRAPE(TimeStep, "alloc")(self->num_of_frames, 0.0, 1.0, "geom");
      GRAPE(self->geometry->object, "create-objects")(Triang1d, "pipe");
    }

    /* allocate memory for the Triang1d instances if necessary */
    step = (TIMESTEP *)self->geometry->object;
    do {
      triang = (TRIANG1D *)step->pre_object;
      if(triang->max_number_of_points < num_points + 1)
        GRAPE(triang, "list-alloc")(num_points + 1);
      step = step->post_step;
    } while(step && step->pre_step->time < step->time);

    /* this is the frame we have to compute */
    triang = (TRIANG1D *)GRAPE(self->geometry->object, "get-pre-obj")
                              (self->time_step[self->frame]);

    /* compute the torus knot */
    delta = 2.0 * M_PI / ggT / num_points;
    r = 0.0;
    for(i = 0; i <= num_points; i++) {
      triang->x[i] = (rad + torad * cos(m * r)) * cos(n * r);
      triang->y[i] = (rad + torad * cos(m * r)) * sin(n * r);
      triang->z[i] = torad * sin(m * r);
      r += delta;
    }
    triang->number_of_points = num_points + 1;

  } else
    printf("time_object_compute_send: unknown object %s\n", self->name);

  END_METHOD(self);
}

If there is more than one Time_Object you work with the method has to find out which object should be computed, this is done by checking the name of the Time_Object instance it is called on.

For the objects of the geometry Chain the method creates a sequence of num_of_frames TimeSteps with Triang1d objects. This is only done once but the memory for the Triang1d instances is checked every time the method is called because the number of points might change. Checking only the step variable as exit criterion in the while loop isn't sufficient since the TimeSteps created by "alloc" are cyclic -- the last is pointing to the first and vice versa.

The part which computes the torus knot is quite simple, but why is the torus knot time-dependent? First of all we store the computed data in the Triang1d instance of the step frame, "compute-video-send" computes all frames by calling "compute-send" for all frame numbers in [0, num_of_frames]. The time-dependency of the double variables is not that obvious, we only access the time_parm-> double_parm values which seem to be static. But every time "set-frame" (and therefore "set-time") is called -- this is done by "compute-video-send" for each frame -- the splines of the double variables are evaluated for the new time and the computed values are written to the variables!

Since curves are used quite often a special class Time_Curve is provided, the corresponding project is described in the following section.


next up previous contents index
Next: Project Curve Up: Project Time-Object Previous: Time and Variable Rulers

SFB 256 Universität Bonn and IAM Universität Freiburg

Copyright © by the Sonderforschungsbereich 256 at the Institut für Angewandte Mathematik, Universität Bonn.