Contour Plots

Last updated : 24 August 2005


This page shows some examples of contour plots obtained with FreeBasic and the FBMath library.


Contouring algorithm

The contouring routine ConRec implemented in FBMath is an adaptation of an algorithm developed by Paul Bourke. The code is the following (file plotcont.bas in FBMath):

' ******************************************************************
' CONREC - A Contouring Routine
' by Paul Bourke, Byte, June 1987
' http://astronomy.swin.edu.au/~pbourke/projection/conrec/

' Adapted to FreeBasic by "Thulefoth"
' Modified by J. Debord for inclusion in the FreeBasic Math library
' http://sourceforge.net/projects/fbmath
' ******************************************************************

' ------------------------------------------------------------------
' Global variable
' ------------------------------------------------------------------

COMMON SHARED ErrCode AS INTEGER
' Error code from the latest function evaluation

' ******************************************************************

OPTION EXPLICIT

SUB ConRec (X()   AS INTEGER, _
            Y()   AS INTEGER, _
            Z()   AS DOUBLE, _
            D()   AS DOUBLE, _
            Col() AS INTEGER)
' ------------------------------------------------------------------
' Input parameters:
' X(0..IUb), Y(0..JUb) = point coordinates in pixels
' D(0..IUb, 0..JUb)    = function values
'                        D(I,J) is the function value at (X(I), Y(I))
' Z(0..(Nc - 1))       = Contour levels in increasing order
' Col(0..(Nc - 1))     = Colour table
' ------------------------------------------------------------------
    
  ' Check the input parameters for validity

  CONST True = -1, False = 0
  
  DIM AS INTEGER PrmErr = False
  DIM AS INTEGER IUb, JUb, Nc, I, J, K, M, M1, M2, M3
  DIM AS DOUBLE  Dmin, Dmax, X1, X2, Y1, Y2

  ErrCode = 0
  
  IUb = UBOUND(X)
  JUb = UBOUND(Y)
  Nc  = UBOUND(Z) + 1
 
  IF IUb <= 0 OR UBOUND(D, 1) <> IUb THEN PrmErr = True
  IF JUb <= 0 OR UBOUND(D, 2) <> JUb THEN PrmErr = True

  IF Nc <= 0 THEN PrmErr = True

  FOR K = 1 TO Nc - 1
    IF Z(K) <= Z(K - 1) THEN PrmErr = True
  NEXT K

  IF PrmErr THEN ErrCode = -1: EXIT SUB

  DIM AS DOUBLE  H(0 TO 4)    ' Relative heights of the box above contour
  DIM AS INTEGER Ish(0 TO 4)  ' Sign of H()
  DIM AS INTEGER Xh(0 TO 4)   ' X coordinates of box
  DIM AS INTEGER Yh(0 TO 4)   ' Y coordinates of box

  ' Mapping from vertex numbers to X offsets
  DIM AS INTEGER Im(0 TO 3) = {0, 1, 1, 0}

  ' Mapping from vertex numbers to Y offsets
  DIM AS INTEGER Jm(0 TO 3) = {0, 0, 1, 1}   

  ' Case switch table
  DIM  AS INTEGER CasTab(0 TO 2, 0 TO 2, 0 TO 2) = _
  {{0,0,8}, {0,2,5}, {7,6,9}, _
   {0,3,4}, {1,3,1}, {4,3,0}, _
   {9,6,7}, {5,2,0}, {8,0,0}}

  ' Scan the array, top down, left to right

  FOR J = JUb - 1 TO 0 STEP -1
    FOR I = 0 TO IUb - 1
      ' Find the lowest vertex
      IF D(I, J) < D(I, J + 1) THEN Dmin = D(I, J) ELSE Dmin = D(I, J + 1)
      IF D(I + 1, J) < Dmin THEN Dmin = D(I + 1, J)
      IF D(I + 1, J + 1) < Dmin THEN Dmin = D(I + 1, J + 1)
   
      ' Find the highest vertex
      IF D(I, J) > D(I, J + 1) THEN Dmax = D(I, J) ELSE Dmax = D(I, J + 1)
      IF D(I + 1, J) > Dmax THEN Dmax = D(I + 1, J)
      IF D(I + 1, J + 1) > Dmax THEN Dmax = D(I + 1, J + 1)
      IF Dmax < Z(0) OR Dmin > Z(Nc - 1) THEN GOTO NoneInBox
   
      ' Draw each contour within this box
      FOR K = 0 TO Nc - 1
        IF Z(K) < Dmin OR Z(K) > Dmax THEN GOTO NoneInTri
       
        FOR M = 4 TO 0 STEP -1
          IF M > 0 THEN
            H(M) = D(I + Im(M - 1), J + Jm(M - 1)) - Z(K)
            Xh(M) = X(I + Im(M - 1))
            Yh(M) = Y(J + Jm(M - 1))
          END IF
       
          IF M = 0 THEN
            H(0) = (H(1) + H(2) + H(3) + H(4)) / 4
            Xh(0) = (X(I) + X(I + 1)) / 2
            Yh(0) = (Y(J) + Y(J + 1)) / 2
          END IF
       
          IF H(M) > 0 THEN Ish(M) = 2
          IF H(M) < 0 THEN Ish(M) = 0
          IF H(M) = 0 THEN Ish(M) = 1
        NEXT M
     
        ' Scan each triangle in the box
        FOR M = 1 TO 4
          M1 = M: M2 = 0: M3 = M + 1
          IF M3 = 5 THEN M3 = 1

          SELECT CASE CasTab(Ish(M1), Ish(M2), Ish(M3))
            CASE 0
              GOTO Case0
            
            ' Line between vertices M1 and M2
            CASE 1
              X1 = Xh(M1): Y1 = Yh(M1): X2 = Xh(M2): Y2 = Yh(M2)

            ' Line between vertices M2 and M3
            CASE 2
              X1 = Xh(M2): Y1 = Yh(M2): X2 = Xh(M3): Y2 = Yh(M3)

            ' Line between vertices M3 and M1
            CASE 3
              X1 = Xh(M3): Y1 = Yh(M3): X2 = Xh(M1): Y2 = Yh(M1)
       
            ' Line between vertex M1 and side M2-M3
            CASE 4
              X1 = Xh(M1): Y1 = Yh(M1)
              X2 = (H(M3) * Xh(M2) - H(M2) * Xh(M3)) / (H(M3) - H(M2))
              Y2 = (H(M3) * Yh(M2) - H(M2) * Yh(M3)) / (H(M3) - H(M2))
       
            ' Line between vertex M2 and side M3-M1
            CASE 5
              X1 = Xh(M2): Y1 = Yh(M2)
              X2 = (H(M1) * Xh(M3) - H(M3) * Xh(M1)) / (H(M1) - H(M3))
              Y2 = (H(M1) * Yh(M3) - H(M3) * Yh(M1)) / (H(M1) - H(M3))

            ' Line between vertex M3 and side M1-M2
            CASE 6
              X1 = Xh(M3): Y1 = Yh(M3)
              X2 = (H(M2) * Xh(M1) - H(M1) * Xh(M2)) / (H(M2) - H(M1))
              Y2 = (H(M2) * Yh(M1) - H(M1) * Yh(M2)) / (H(M2) - H(M1))
       
            ' Line between sides M1-M2 and M2-M3
            CASE 7
              X1 = (H(M2) * Xh(M1) - H(M1) * Xh(M2)) / (H(M2) - H(M1))
              Y1 = (H(M2) * Yh(M1) - H(M1) * Yh(M2)) / (H(M2) - H(M1))
              X2 = (H(M3) * Xh(M2) - H(M2) * Xh(M3)) / (H(M3) - H(M2))
              Y2 = (H(M3) * Yh(M2) - H(M2) * Yh(M3)) / (H(M3) - H(M2))
       
            ' Line between sides M2-M3 and M3-M1
            CASE 8
              X1 = (H(M3) * Xh(M2) - H(M2) * Xh(M3)) / (H(M3) - H(M2))
              Y1 = (H(M3) * Yh(M2) - H(M2) * Yh(M3)) / (H(M3) - H(M2))
              X2 = (H(M1) * Xh(M3) - H(M3) * Xh(M1)) / (H(M1) - H(M3))
              Y2 = (H(M1) * Yh(M3) - H(M3) * Yh(M1)) / (H(M1) - H(M3))
       
            ' Line between sides M3-M1 and M1-M2
            CASE 9
              X1 = (H(M1) * Xh(M3) - H(M3) * Xh(M1)) / (H(M1) - H(M3))
              Y1 = (H(M1) * Yh(M3) - H(M3) * Yh(M1)) / (H(M1) - H(M3))
              X2 = (H(M2) * Xh(M1) - H(M1) * Xh(M2)) / (H(M2) - H(M1))
              Y2 = (H(M2) * Yh(M1) - H(M1) * Yh(M2)) / (H(M2) - H(M1))
          END SELECT
    
          LINE (X1, Y1)-(X2, Y2), Col(K)
Case0:
        NEXT M
NoneInTri:
      NEXT K
NoneInBox:
    NEXT I
  NEXT J
END SUB


Example 1

An example program is shown below (file contour.bas in FBMath).

The example function is:
f(x,y) = sin
Ö
 

x2 + y2
 
+ 0.5 /
Ö
 

(x + c)2 + y2
 

where c is a constant which may be varied to modify the aspect of the graph (the example used c = 3.05).

' ******************************************************************
' Contour plot of a two-dimensional function
' ******************************************************************

#include "math.bi"
#include "fbgfx.bi"

' ******************************************************************

OPTION EXPLICIT

' ******************************************************************
' Define here the function to be plotted
' ******************************************************************

CONST FuncName = "SIN(SQR(X^2 + Y^2)) + 0.5 / SQR((X + 3.05)^2 + Y^2)"

FUNCTION Func(X AS DOUBLE, Y AS DOUBLE) AS DOUBLE
' Function to be plotted
  
  CONST C = 3.05
  
  DIM AS DOUBLE X2, Y2, Xc, Xc2
  
  X2 = X * X
  Y2 = Y * Y
  Xc = X + C
  Xc2 = Xc * Xc
  
  Func = SIN(SQR(X2 + Y2)) + 0.5 / SQR(Xc2 + Y2)
END FUNCTION

' ******************************************************************
' Main program
' ******************************************************************

' Initialize screen (Screen 12 with white on black here)

IF NOT InitScreen(12, 15, 0) THEN STOP

' Set number of contours (colours) according to the screen resolution

CONST Nc = 15

' Define colours

DIM AS INTEGER Col(0 TO Nc - 1), I

FOR I = 0 TO Nc - 1 
  Col(I) = Nc - I
NEXT I

' Set scale

CONST Xmin = - TwoPi, Xmax = TwoPi, Xstep = Pi
CONST Ymin = - TwoPi, Ymax = TwoPi, Ystep = Pi

' Set graphic area (here it will look approximately square)

InitGraph 24, 76, 15, 85

' Set number of grid points

CONST NpX = 60
CONST NpY = 60

' Dimension variables

DIM AS INTEGER J                           ' Loop variable
DIM AS DOUBLE  Dx, Dy                      ' Increments of X and Y
DIM AS DOUBLE  Dmin, Dmax                  ' Min. and max. function values
DIM AS DOUBLE  H                           ' Increment for levels

DIM AS DOUBLE  Xu(0 TO NpX), Yu(0 TO NpY)  ' Point coordinates in user units
DIM AS INTEGER Xp(0 TO NpX), Yp(0 TO NpY)  ' Point coordinates in pixels  
DIM AS DOUBLE  D(0 TO NpX, 0 TO NpY)       ' Function values
DIM AS DOUBLE  Z(0 TO Nc - 1)              ' Contour array

' Set scales and plot axes

SetOxScale LinScale, Xmin, Xmax, Xstep
SetOyScale LinScale, Ymin, Ymax, Ystep

PlotOxAxis Ymin, 2
PlotOyAxis Xmin, 2

PlotGrid BothGrid

WriteTitles FuncName, "X", "Y"

' Generate grid points

Dx = (Xmax - Xmin) / NpX
Dy = (Ymax - Ymin) / NpY

Xu(0) = Xmin
FOR I = 1 TO NpX
  Xu(I) = Xu(I - 1) + Dx
NEXT I

Yu(0) = Ymin
FOR J = 1 TO NpY
  Yu(J) = Yu(J - 1) + Dy
NEXT J

FOR I = 0 TO NpX
  Xp(I) = Xpixel(Xu(I))
NEXT I

FOR J = 0 TO NpY
  Yp(J) = Ypixel(Yu(J))
NEXT J

' Compute function values

Dmin = Func(Xmin, Ymin)
Dmax = Dmin

FOR I = 0 TO NpX
  FOR J = 0 TO NpY
    D(I,J) = Func(Xu(I), Yu(J))
    IF D(I,J) < Dmin THEN 
      Dmin = D(I,J)
    ELSEIF D(I,J) > Dmax THEN
      Dmax = D(I,J)
    END IF
  NEXT J  
NEXT I
    
' Define levels

Z(0) = Dmin

H = (Dmax - Dmin) / (Nc - 1)

FOR I = 1 TO Nc - 1 
  Z(I) = Z(I - 1) + H
NEXT I

' Plot contour

ConRec Xp(), Yp(), Z(), D(), Col()

SLEEP
END


This program produced the following image:



Example 2

Another example, with a function having multiple minima and maxima:

f(x,y) = x2 + y2 + cos ax + cos by

where a and b are constants.

In the case (a = 5, b = 8), the previous program should be modified according to the following code:

CONST FuncName = "X2 + Y2 + COS(5 * X) + COS(8 * Y)"

FUNCTION Func(X AS DOUBLE, Y AS DOUBLE) AS DOUBLE
' Function to be plotted
  
  DIM AS DOUBLE X2, Y2
  
  X2 = X * X
  Y2 = Y * Y
  
  Func = X2 + Y2 + COS(5 * X) + COS(8 * Y)
END FUNCTION

CONST Xmin = - 1, Xmax = 1, Xstep = 0.5
CONST Ymin = - 1, Ymax = 1, Ystep = 0.5


and the output of the program would be the following:



You can create interesting effects by:


File translated from TEX by TTH, version 3.68.
On 22 Aug 2005, 17:34.