next up previous contents index
Next: Putting It All Together Up: Interactive Elements Previous: Display Methods Using Items

A User-Interface for Rot2d

What do we need for working with Rot2d instances? First of all buttons for sending the methods "rotate", "insert-point" and "delete-point" are needed, additionally we create a "reset" button which resets the surface to its initial state. The rulers for angle, discretization and current point are no problem, but editing the point is more difficult: the rulers will be bound to the current point coordinate variables, but when the current point is changed these rulers have to be bound to new variables. Therefore we first provide a method for changing the coordinate ruler variables:

/*
 * The coordinate rulers are the most difficult part of the user interface,
 * because the point that is edited can be changed (by a ruler). We need
 * them in the method "update-point", so we make them global.
 */
static RULER *xpos, *ypos, *zpos;


ROT2D *rot2d_update_point()
{
  ROT2D *self;

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

  /*
   * Again: check if there is a curve. In this case it is very
   * important to test if current is a valid index, because
   * we used it to access the coordinate array.
   */
  if(!self->curve || self->current < 0 ||
     self->current > self->curve->number_of_points - 1)
    END_METHOD(NULL);

  /*
   * Now bind the coordinate ruler to the coordinates of the
   * (new) current point. They will be updated automatically.
   */
  GRAPE(xpos, "set-variable")(&self->curve->x[self->current]);
  GRAPE(ypos, "set-variable")(&self->curve->y[self->current]);
  GRAPE(zpos, "set-variable")(&self->curve->z[self->current]);

  END_METHOD(self);
}

The method "set-variable" is sent to the rulers to update their variables, of course we first have to check if the current point is valid.

We use a method to create the buttons and rulers, otherwise the main program would get rather long. The rulers are bound to the Rot2d instance the method is send to:

ROT2D *rot2d_get_editor()
{
  ROT2D *self;
  MANAGER *mgr;
  RULER *current, *angle, *discr;
  BUTTON *rotate, *reset, *insert, *delete;

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

  /*
   * Again these checks... But try to remove them and set current
   * to something like -10000, this will surely crash the program.
   * We need a curve, points and a valid current value.
   */
  if(!self->curve || !self->curve->number_of_points ||
     self->current < 0 || self->current > self->curve->number_of_points - 1)
    END_METHOD(NULL);

  /*
   * Like in Rot2d "display" we need the manager for adding
   * our rulers and buttons.
   */
  ASSURE(mgr = (MANAGER *)GRAPE(Manager, "get-stdmgr")(),
         "rot2d_get_editor: can't get mgr", END_METHOD(NULL));

  /*
   * First we create rulers for the current point, the coordinates
   * of the current point, the angle and the discretization. The
   * coordinate rulers have to be updated when the current point
   * is changed, see below.
   */
  current = (RULER *)GRAPE(Ruler, "new-instance")
    (&self->current, "point", dfInt);
  xpos = (RULER *)GRAPE(Ruler, "new-instance")
    (&self->curve->x[self->current], "x pos", dfDouble);
  ypos = (RULER *)GRAPE(Ruler, "new-instance")
    (&self->curve->y[self->current], "y pos", dfDouble);
  zpos = (RULER *)GRAPE(Ruler, "new-instance")
    (&self->curve->z[self->current], "z pos", dfDouble);
  angle = (RULER *)GRAPE(Ruler, "new-instance")
    (&self->angle, "angle", dfDouble);
  discr = (RULER *)GRAPE(Ruler, "new-instance")
    (&self->discr, "discr", dfInt);

  /*
   * Now the buttons, the don't get any special object, so they
   * will send their method to the current object. This might be
   * dangerous, because someone can change to the curve or axis
   * and send "insert-point" or "delete-point" to it by pressing
   * the button. What about the parameter in this case?
   */
  rotate = (BUTTON *)GRAPE(Button, "new-instance")
    ("rotate-send", NULL, "rotate");
  reset = (BUTTON *)GRAPE(Button, "new-instance")
    ("reset", NULL, "reset");
  insert = (BUTTON *)GRAPE(Button, "new-instance")
    ("insert-point", NULL, "insert-point");
  delete = (BUTTON *)GRAPE(Button, "new-instance")
    ("delete-point", NULL, "delete-point");

  /*
   * Creating the rulers and buttons has never
   * failed before, but who nows...
   */
  ASSURE(current && xpos && ypos && zpos && angle && discr &&
         rotate && reset && insert && delete,
         "rot2d_get_editor: can't create ruler/button", END_METHOD(NULL));

  /*
   * This is the most interesting part. We tell the current ruler
   * to call "update-point" on the Rot2d instance when it is changed
   * by the user. This method will bind the coordinate rulers to
   * the new current point, see below.
   */
  GRAPE(current, "set-instance")(self);
  GRAPE(current, "set-method")("update-point");

  /*
   * We define a suitable size for the buttons - half the width
   * of the standard control window. The rulers default size is ok.
   */
  GRAPE(rotate, "set-pref-size")(6.0, 1.0);
  GRAPE(reset, "set-pref-size")(6.0, 1.0);
  GRAPE(insert, "set-pref-size")(6.0, 1.0);
  GRAPE(delete, "set-pref-size")(6.0, 1.0);

  /*
   * Finally add all items to the option menu of the manager.
   */
  GRAPE(mgr, "add-inter")(current);
  GRAPE(mgr, "add-inter")(xpos);
  GRAPE(mgr, "add-inter")(ypos);
  GRAPE(mgr, "add-inter")(zpos);
  GRAPE(mgr, "add-inter")(angle);
  GRAPE(mgr, "add-inter")(discr);
  GRAPE(mgr, "add-inter")(rotate);
  GRAPE(mgr, "add-inter")(reset);
  GRAPE(mgr, "add-inter")(insert);
  GRAPE(mgr, "add-inter")(delete);

  END_METHOD(self);
}

The most interesting part are the lines where the current ruler is configured to send the method "update-point" to the Rot2d instance whenever it it changed (with the mouse or by typing in a new value):

GRAPE(current, "set-instance")(self);
GRAPE(current, "set-method")("update-point");
This ensures that the coordinate rulers always point to the right variables.



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.