next up previous contents index
Next: Interactive Elements Up: Writing Methods Previous: The "universal" Method

More Methods for Rot2d

Now we have written the memory management, copy and display methods for our class Rot2d and set up its interactive environment. Now we will write the main method which computes the surface of revolution from the curve, rotation angle and discretization. Watch how existing methods and utility functions are used to simplify the method.

ROT2D *rot2d_rotate_send()
{
  ROT2D *self;
  TRIANG1D *curve, *axis;
  MATRIX44 rotseg, rot, prod;
  VEC3 paxis[2], prot;
  int number_of_points;
  int number_of_elements;
  int ulines, vlines;
  int i, j, ind;
  double angle;

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

  /*
   * Before we start computing the surface some check have to be done.
   * Curve and axis must exist and have enough resp. the right number
   * of points. Creating a negative or zero number of segments doesn't
   * make much sense and we need a non-zero angle.
   */
  ASSURE((curve = self->curve) && curve->number_of_points > 1,
         "rot2d_rotate_send: no curve with enough points",
         END_METHOD(NULL));
  ASSURE((axis = self->axis) && axis->number_of_points == 2,
         "rot2d_rotate_send: no correct axis found",
         END_METHOD(NULL));
  ASSURE(self->discr > 0,
         "rot2d_rotate_send: need discr > 0",
         END_METHOD(NULL));
  ASSURE(self->angle != 0.0,
         "rot2d_rotate_send: need angle != 0",
         END_METHOD(NULL));

  /*
   * First the number of points and elements is computed. For discr
   * segments we have discr + 1 lines, the number of lines in the
   * other direction is given by the curve.
   */
  ulines = self->discr + 1;
  vlines = curve->number_of_points;

  /*
   * A grid with each quadrilateral divided into two triangles
   * is used, so we need these numbers of points an elements:
   */
  number_of_points = ulines * vlines;
  number_of_elements = 2 * (ulines - 1) * (vlines - 1);

  /*
   * Now check if this Rot2d instance has enough memory to store
   * the new triangulation. We compare both the computed number
   * of points and triangles to the maximum number available.
   */
  if(self->max_number_of_points < number_of_points ||
     self->max_number_of_elements < number_of_elements)
    /*
     * If there isn't enough memory we just extend the old memory
     * lists by calling "list-alloc". We will overwrite the old data
     * which is preserved by "list-alloc" later.
     */
    GRAPE(self, "list-alloc")(number_of_points, number_of_elements);

  /*
   * The information about how much of the memory of the instance
   * is used has to be stored in the instance, "list-alloc" only
   * sets the maximum number of points and elements to the array
   * sizes.
   */
  self->number_of_points = number_of_points;
  self->number_of_elements = number_of_elements;

  /*
   * Each segment of the surface is angle / discr wide. g_line_rotate
   * computes the rotation matrix for a rotation around and arbitrary
   * axis. Unfortunately we have to copy the end points of the axis
   * because this function needs an array of two VEC3. The other para-
   * meters are the angle and the matrix that has to be computed.
   * The rotseg matrix describes the rotation for one segment, the
   * rotation matrix rot is set to the identity matrix because we
   * start with no rotation.
   */
  angle = (2.0 * M_PI / 360.0) * self->angle / (double)self->discr;
  paxis[0][0] = axis->x[0]; paxis[0][1] = axis->y[0]; paxis[0][2] = axis->z[0];
  paxis[1][0] = axis->x[1]; paxis[1][1] = axis->y[1]; paxis[1][2] = axis->z[1];
  g_line_rotate(paxis, angle, rotseg);
  g_matrix44_set_identity(rot);

  /*
   * Now we compute the points. For each segment was have to transform
   * the points of the curve with the current rotation matrix. Again
   * we have to copy the points from and to the triangulation to use
   * the matrix multiplication function (alternatively we could have
   * done the multiplication by hand). After each segment the rotation
   * matrix is multiplied with the segment rotation matrix to get the
   * matrix needed for the next segment.
   */
  for(ind = 0, i = 0; i < ulines; i++) {
    for(j = 0; j < vlines; j++, ind++) {
      prot[0] = curve->x[j]; prot[1] = curve->y[j]; prot[2] = curve->z[j];
      g_matrix44_mult_vec3(rot, prot);
      self->x[ind] = prot[0]; self->y[ind] = prot[1]; self->z[ind] = prot[2];
    }
    g_matrix44_mult(prod, rotseg, rot);
    g_matrix44_assign(prod, rot);
  }

  /*
   * We also have to define the connectivity of the triangulation
   * (which points belong to which triangle). Again we run through
   * the segments storing two triangles for each quadrilateral.
   * It's really quite simple in this case...
   */
  for(i = 0; i < ulines - 1; i++)
    for(j = 0; j < vlines - 1; j++) {
      ind = (i * (vlines - 1) + j) * 2;
      self->vertex[ind][0] = i * vlines + j;
      self->vertex[ind][1] = (i + 1) * vlines + j;
      self->vertex[ind][2] = i * vlines + j + 1;
      ind++;
      self->vertex[ind][0] = i * vlines + j + 1;
      self->vertex[ind][1] = (i + 1) * vlines + j;
      self->vertex[ind][2] = (i + 1) * vlines + j + 1;
    }

  /*
   * Some methods also need information about the neighbours of the
   * triangles. Computing this directly is no problem, but since
   * we have a method for this we just call it.
   */
  GRAPE(self, "make-neighbour-send")();

  END_METHOD(self);
}

Creating the surface is one part of the job, but first we have to create a curve we can rotate (the default curve is not very interesting tex2html_wrap_inline43942 ). The interactive editing is described in the next section, here we present the methods for inserting points in and deleting them from a Triang1d instance. The methods could be made more efficient by using the system function memmove, but this way they are easier to understand (they won't be called too often with too many points anyway).

TRIANG1D *triang1d_insert_point(int number)
{
  TRIANG1D *self;
  int i;

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

  /*
   * First some checks. Since we insert the new point at position
   * "number", this index can not be the first point, it has to be
   * in [1, number of points - 1].
   */
  if(number <= 0 || number > self->number_of_points - 1) {
    fprintf(stderr,
            "triang1d_insert_point: can't insert point at position %d\n",
            number);
    END_METHOD(NULL);
  }

  /*
   * If we have already used all points in this triang1d instance we
   * need new memory for the new point. "list-alloc" will do the job
   * because for Triang1d it will copy the coordinates to the new
   * arrays, therefore we lose no information and get more memory!
   * Be careful: there are still some "list-alloc" methods (e.g. on
   * List_Of_Inst) which will not preserve the old data.
   */
  if(self->number_of_points == self->max_number_of_points)
    GRAPE(self, "list-alloc")(self->max_number_of_points + 1);

  /*
   * We have to shift the points from index number on to get a free place
   * for storing the new point (memmove would probably be faster)
   */
  for(i = self->number_of_points - 1; i >= number; i--) {
    self->x[i + 1] = self->x[i];
    self->y[i + 1] = self->y[i];
    self->z[i + 1] = self->z[i];
  }

  /*
   * The new point is interpolated from the two points next to it,
   * its just the midpoint of the line between these points
   */
  self->x[number] = (self->x[number - 1] + self->x[number + 1]) / 2.0;
  self->y[number] = (self->y[number - 1] + self->y[number + 1]) / 2.0;
  self->z[number] = (self->z[number - 1] + self->z[number + 1]) / 2.0;

  /*
   * Now we have an additional point...
   */
  self->number_of_points++;

  END_METHOD(self);
}


TRIANG1D *triang1d_delete_point(int number)
{
  TRIANG1D *self;
  int i;

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

  /*
   * Again first the checks. We don't want curves with no points or just
   * one point, and we have to make sure that the index number is valid.
   */
  if(self->number_of_points <= 2) {
    fprintf(stderr, "triang1d_delete_point: less than three points left\n");
    END_METHOD(NULL);
  }
  if(number < 0 || number > self->number_of_points - 1) {
    fprintf(stderr, "triang1d_delete_point: "
                    "can't delete point at position %d\n", number);
    END_METHOD(NULL);
  }

  /*
   * Like in "insert-point" we shift the points from index number
   * on, but now in the other directions. And we have one points less.
   */
  for(i = number; i < self->number_of_points - 1; i++) {
    self->x[i] = self->x[i + 1];
    self->y[i] = self->y[i + 1];
    self->z[i] = self->z[i + 1];
  }
  self->number_of_points--;

  END_METHOD(self);
}

The next two methods are the class Rot2d counterpart to the "insert-point" and "delete-point" methods on class Triang1d. They can be called interactively (we will put them on a button) because they have no parameters, this is not possible for the Triang1d methods. For the moment you should ignore the calls to "update-point", this method will be introduced in the next section.

ROT2D *rot2d_insert_point()
{
  ROT2D *self;

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

  /*
   * We definitely need a curve to insert a point.
   */
  if(!self->curve || !self->curve->number_of_points)
    END_METHOD(NULL);

  /*
   * We just call "insert-point" on the curve with the current
   * point as parameter to insert the point. Now the coordinate
   * rulers should show the coordinates of the new point,
   * therefore we update them.
   */
  if(GRAPE(self->curve, "insert-point")(self->current))
    GRAPE(self, "update-point")();

  END_METHOD(self);
}


ROT2D *rot2d_delete_point()
{
  ROT2D *self;

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

  /*
   * We need a curve for delete, too.
   */
  if(!self->curve || !self->curve->number_of_points)
    END_METHOD(NULL);

  /*
   * Just like insert, again we have to update the rulers,
   * because we have changed the current point.
   */
  if(GRAPE(self->curve, "delete-point")(self->current))
    GRAPE(self, "update-point")();

  END_METHOD(self);
}

Now we have all we need to create and edit Rot2d instances, but this can up to now only be done by programming. To be able to work interactively we have to add an interface to the methods and variables to the GRAPE manager.



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.