Programs for Programmers

FTN95.NET Example Code

Mandlebrot Example

The documentation on this page is taken from the FTN95 User Guide. It is presented here to demonstrate the quality of documentation along with the ease in which .NET code can be used from Fortran.

Topics Covered

Calling Fortran from other .NET languages
Passing .NET objects to Fortran
Calling .NET methods from Fortran

Introduction

This example demonstrates many features of FTN95 for .NET and .NET language interoperability. A C# Windows Application makes use of a FTN95 for .NET assembly which makes calls to the .NET Framework to perform drawing operations.

This program requires the Microsoft .NET Framework to be installed in order to compile and run it. You can compile this example program either in Microsoft Visual Studio .NET or from the command line using the supplied batch files.

To compile from Microsoft Visual Studio .NET you just need to load the supplied solution file (Mandelbrot.sln), then choose Build Solution from the Build menu. You can now run the program under Debug mode by pressing F5.

To compile from the command line you can use either of the two batch files included in the Mandelbrot directory. Run builddbg.bat to create a debug build of the program in the Mandelbrot\bin\Debug directory, or run buildrel.bat to create a release build of the program in the Mandelbrot\bin\Release directory.

If compiling this application from the command line you should ensure that the .NET Framework directory is on the system PATH. Running the FTN95 for Microsoft .NET Command Prompt from the start menu will ensure that this is set up correctly.

Example Creation

This example makes use of two languages. A C# Windows Application and an FTN95 Fortran Application Extension project.

To create the Fortran Application Extension project, select New Project from the File menu. One file will be added to the project when it is created. This project uses functions that are available in Windows.System.Forms.dll, you should add this as a reference to the project using Add Reference from the References node. Enter the code below into the source file. This project can now be built using the Build command from the project menu.

To create the C# application, select New Project from the File menu, name the application if desired or accept the default. From the Toolbox add a PictureBox control and a Button control. Double-click the Button control to add an event handler for that button. Add the C# code as shown below. Because the C# application makes a call into the Fortran Application Extension you need to add a reference to the Fortran project. Do this by selecting Add Reference from the References node within the C# application and selecting the Fortran project.

Code Features

This example demonstrates calling a Fortran assembly from C# and passing .NET objects from the C# application. Methods of these objects are then called by the Fortran code to perform actions on the C# form.

 

The Fortran source is examined in more detail below.

Source Code


Fortran Source

!-----------------------------------------------------
! Module NetDeclarations contains declarations of .NET
! methods called by this example
!-----------------------------------------------------
MODULE NetDeclarations
  OBJECT("System.Drawing.Bitmap") :: bitmap
  OBJECT("System.Windows.Forms.PictureBox") :: picturebox
  ASSEMBLY_EXTERNAL(name="System.Drawing.Bitmap.SetPixel") :: bitmapSetPixel
  ASSEMBLY_EXTERNAL(name="System.Windows.Forms.PictureBox.Update") :: pictureboxUpdate
  ASSEMBLY_EXTERNAL(name="System.Windows.Forms.PictureBox.Invalidate") :: pictureboxInvalidate
  ASSEMBLY_EXTERNAL(name="System.Drawing.Color.FromArgb") :: colorFromArgb
  INTEGER :: r(256)
  INTEGER :: g(256)
  INTEGER :: b(256)
END MODULE
!-----------------------------------------------------
! Setup .NET objects
!-----------------------------------------------------
SUBROUTINE SetupMandelbrot_(bitmap_,picturebox_)
  USE NetDeclarations
  ASSEMBLY_INTERFACE(name="SetupMandelbrot")
  OBJECT("System.Drawing.Bitmap") :: bitmap_
  OBJECT("System.Windows.Forms.PictureBox") :: picturebox_
  INTEGER :: i
  bitmap = bitmap_
  picturebox = picturebox_
  DO i=1,192
    r(i) = MIN(255, (i*4/3) +192)
    g(i) = MIN(255, (i*4/3) +64)
    b(i) = MIN(255, (i*4/3))
  ENDDO
  DO i=1,64
    r(192+i) = MIN(255, (65-i)*4 + 192/3)
    g(192+i) = MIN(255, (65-i)*4 + 64/3)
    b(192+i) = MIN(255, (65-i)*4)
  ENDDO
END SUBROUTINE

This module defines a TYPE that will be used by main routine and also declares ASSEMBLY_EXTERNAL aliases for several .NET methods that are used by the main routine. Details of another routine called to setup items for later use such as a color matrix can also be seen

!-----------------------------------------------------
! Draws the image on the Bitmap object we have been
! passed in
!-----------------------------------------------------
SUBROUTINE DrawMandelbrot_(zoom,offset_x,offset_y,size)
  USE NetDeclarations
  ASSEMBLY_INTERFACE(name="DrawMandelbrot")

.NET Objects are passed to the Fortran routine as bitmap and picturebox. Their full type can be seen in the OBJECT statements in the code above. An external alias is provided for the routine by use of an ASSEMBLY_INTERFACE statement.

  INTEGER i,j,k,size,iter
  DOUBLE PRECISION :: zoom
  DOUBLE PRECISION :: offset_x,offset_y
  DOUBLE PRECISION :: zr,cr,zi,ci,zis,zrs
  i = 1
  j = 1
  iter = size
  size = size/2
  offset_x = offset_x-200.0+size
  offset_y = offset_y-120.0+size
  DO WHILE (i < iter+1)
    j = 1
    DO WHILE (j < iter+1)
      cr = ((i-size)/zoom + offset_x)/100.0
      ci = ((j-size)/zoom + offset_y)/100.0
      zr = 0.
      zi = 0.
      DO k=1,255
        zis = zi*zi
        zrs = zr*zr
        zi = 2*zr*zi+ci
        zr = zrs-zis+cr
        IF ((zrs+zis)>4.) EXIT
      ENDDO

The above is standard Fortran code.

      CALL bitmapSetPixel(bitmap,i-1,j-1,colorFromArgb(r(k),g(k),b(k)))
      j = j+1 
    ENDDO 
    i = i+1
  ENDDO
  CALL pictureboxInvalidate(picturebox)
  CALL pictureboxUpdate(picturebox)
END

The code above demonstrates the calling of .NET instance methods. The methods called do not return a value and so can be called as a SUBROUTINE. The aliases provided in the module are used and any arguments that are required are passed to the routine. Note that as these are instance methods the object that the method is being called on has to be passed as the first argument to the method.

This Fortran source should be compiled as a Fortran Application Extension and added to the following C# Application as a reference.

C# Source

//
// Automatically generated code removed
//

private void SetupBitmap()
{
  bitmap1 = new Bitmap(255,255);
  pictureBox1.Image = bitmap1;
}

private void DrawMandlebrot()
{
  MandelbrotBackend.DrawMandelbrot(zoom, x, y, pictureBox1.Height-1);
}

private void SetupMandelbrot()
{
  MandelbrotBackend.SetupMandelbrot(bitmap1, pictureBox1);
}

private void Form1_Load(object sender, System.EventArgs e)
{
  SetupBitmap();
  SetupMandelbrot();
  DrawMandlebrot();
}

private void Form1_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
{
  if (e.KeyCode == Keys.Down)
    y += 20/zoom;
  else if (e.KeyCode == Keys.Up)
    y -= 20/zoom;
  else if (e.KeyCode == Keys.Left)
    x -= 20/zoom;
  else if (e.KeyCode == Keys.Right)
    x += 20/zoom;
  else if (e.KeyCode == Keys.PageUp)
    zoom *= 1.25;
  else if (e.KeyCode == Keys.PageDown)
    zoom /= 1.25;
  DrawMandlebrot();
}