Graphics
Sphere In Cube Movie  Lab Report
Create and Display "Movie" of Sphere in a Cube

Purpose
The "Sphere in Cube Movie" program generates an animation sequence (50 frames by default) to show a "fly around" of a sphere in a cube.  This "stack" of images can be saved to an animated GIF file.

Materials and Equipment

Software Requirements
Tested with Windows 2000, Delphi 7 (to recompile)   [Should work in Win 95 or late, Delphi 3 or later]
SphereInCubeMovie.EXE

Hardware Requirements
VGA display

Procedure

  1. Make sure you have sufficient space for the swap file. Generating a large number of frames can tax the virtual memory system. (32 MB memory and, perhaps, 64 MB of disk space should be sufficient to run this program.)
  2. Double click on the SphereInCubeMovie.EXE icon to start the program.
  3. Press the Generate button and wait while the 50 image frames are created.  [The user interface has a max of 750.]
  4. Press the Show button to see the animation sequence as quickly as possible.
  5. If desired, change the minimum milliseconds/frame spinbox to slow down the display of the animation sequence and press Show again.
  6. If desired, use the ScrollBar at the bottom to select any image in the sequence.
  7. If desired, select the Make GIF button and specify a filename.
  8. If desired, change the number of frames and repeat any of the above steps.

Discussion
Clicking the Generate button results in a call to the ButtonGenMovieClick method.  In a FOR loop from 0 to SpinEditMaxFrames.Value-1, the following code defines a ViewTransformMatrix, which automatically transforms each point plotted by DrawCube and DrawSphere as part of the default transformation of the TPantograph:

  // Use FrameIndex in parametric equation to define location of "camera"
  // for each frame.
  a := ViewTransformMatrix (
            // Use spherical coordinates instead of cartesian
            coordSpherical,
            // azimuth -- cycle around object every 300 Frames
            ToRadians(1.2 {degrees} * FrameIndex),
            // elevation -- cycle up and down 90 degrees every 90 Frames
            ToRadians(90 {degrees} * SIN(ToRadians(4.0*FrameIndex))),
            // distance from camera to object -- cycle every 400 Frames
            10 + 6*SIN(ToRadians(0.9*FrameIndex)),
            // screen parameters: 10x10 from 30 away
             10,10,30);
  pantograph.SetTransform (a);
  DrawCube(PantoGraph, clRed);
  DrawSphere(PantoGraph,
             {LatitudeColor}         clBlue,
             {LongitudeColor}        clLime,
             {LatitudeCircles}        9,
             {LongitudeSemicircles}  25,
              {PointsInCircle}        40);

A TList of Bitmaps, BitmapList, is used to store the sequence of bitmaps.  An EOutOfMemory or an EOutOfResource error should be trapped if sufficient resources are not present.

The Show button steps through TList and sets the ScrollBar.Position. The side effect of setting the ScrollBar.Position is to display the appropriate frame in the animation sequence.

procedure TFormSphereMovie.ScrollBarChange(Sender: TObject);
begin
  WITH ImageMovieFrame.Picture.Bitmap DO
  BEGIN
    // Copy in-memory frame back to screen
    Canvas.CopyRect(Rect(0,0, Width,Height),
                    TNode(BitMapList.Items[ScrollBar.Position]).BitMap.Canvas,
                    Rect(0,0, Width,Height));
  END;
end; 

The animated "stack" can be saved to a GIF file using code courtesy of Finn Tolderlund in the GIFAnimate.PAS unit. Also required is  Anders Melander's GIFImage component.  The GIFImage unit can be downloaded from Finn Tolderlund's websites:

http://finn.mobilixnet.dk/delphi
http://home20.inet.tele.dk/tolderlund/delphi

If you do not have TGIFImage installed, change the conditional compilation variable form "GIF" to "NOGIF" (Project | Options | Directories/Conditionals | Conditioanl Defines = NOGIF).

// Thanks to Finn Tolderlund for adding this animated GIF improvement. 
// This change requires the GIFImage unit from Anders Melander. 
procedure TFormSphereMovie.MakeGifButtonClick(Sender: TObject);
{$IFDEF GIF}
var
  FrameIndex: Integer;
  Picture: TPicture;
{$ENDIF}
begin
{$IFDEF GIF}
  SavePictureDialog.InitialDir := ExtractFilePath(ParamStr(0));
  IF   SavePictureDialog.Execute
  THEN BEGIN
    Screen.Cursor := crHourGlass;
    try
      GifAnimateBegin;
      {Step through each frame in in-memory list}
      for FrameIndex := 0 to BitMapList.Count - 1 do
      begin
        // show user that something is happening
        ScrollBar.Position := FrameIndex;
        Application.ProcessMessages;
        // add frame to animated gif
        GifAnimateAddImage(TNode(BitmapList.Items[FrameIndex]).Bitmap,
          False, SpinEditMinMillisecondsPerFrame.Value);
      end;
      // We are using a TPicture but we could have used a TGIFImage instead.
      // By not using TGIFImage directly we do not have to add GIFImage 
      // to the uses clause.
      // By using TPicture we only need to add GifAnimate to te uses clause.
      Picture := GifAnimateEnd;
      try
        Picture.SaveToFile(SavePictureDialog.Filename);  // save gif
        ImageMovieFrame.Picture.Assign(Picture);         // display gif
      finally
        Picture.Free;
      end;
    finally
      Screen.Cursor := crDefault;
    end;
  END
{$ENDIF}
end;


The GetTickCount API call is used to make sure a minimum amount of time is used to display each frame in the stack.

The DrawFigures unit can be used to draw a cube, sphere, 3D-surface, or a football field.

Conclusions
A simple animation sequence can be produced by creating TBitmaps that are displayed in a TImage as quickly as possible, or saved to a single GIF file as an animated sequence.


Keywords
2D/3D vector graphics, translation, rotation, scaling, view transform, homogeneous coordinates, clipping, projections, vectors, matrices, TPantograph, GraphicsPrimitivesLibrary, GraphicsMathLibrary, Delphi 

Download
Delphi 7 Source and EXE: SphereInCubeMovie.ZIP


Updated
26 Feb 2005


since 1 Nov 1998