Sales pitch for Octave

For plotting I personally prefer Octave. Mathematica, Maxima, and Axiom can all provide good plots on most occasions, but it is Octave that I return to time after time to create plots. I prefer it so much that I’ve put together a document of examples showing the kinds of graphs and graph manipulations that may be achieved using Octave. You can download the most recent version here: pdf/ps

Plotting functions of one variable (2D plotting)

Mathematica

In[7]:= Plot[ Sin[x], {x, -1, 10 } ]

Maxima

(%i1) plot2d( sin(x), [x, -1, 10] );

If a graph is too tall an needs to be truncated, you can also specify a range. Contrast the following 2 plots:

(%i2) plot2d( sec(x), [x,-%pi, %pi ] );
(%i3) plot2d( sec(x), [x,-%pi, %pi ], [y,-20,20] );

Axiom

(1) -> draw( sin(x), x=-1..10 )

Octave

Since Octave is not a computer algebra system, the way it handles graphing is a bit different—you can’t just pass it an algebraic expression. Instead, the plot command in Octave accepts two vectors (one-dimensional arrays). The first contains all of the x-coordinates of points to plot and the second contains all of the y-coordinates. Because of the way Octave does arithmetic, these are easy to generate:

octave:7> x = [ -1 : 0.1 : 10 ];
octave:8> y = sin(x);
octave:9> plot(x,y)

Sine curve plotted with octave

Here x is defined to be an array containing values from -1 to 10, counting by 0.1, i.e. [ -1, -0.9, …, 9.8, 9.9, 10 ]. Then y is set to the array containing the sine of each of these values (it is common for functions to act coordinate-wise in Octave). Finally, the plot is drawn from points with x-coordinates coming from x and y-coordinates coming from y.

Plotting a parameterized curve in 2-space

Mathematica

Plotting a parameterized curve in Mathematica is as easy as using the ParametricPlot function. For example, to get the plot of a circle,

In[1]: ParametricPlot[ { Cos[t], Sin[t] }, {t,0,2*Pi} ]

Maxima

Plotting a parameterized curve in Maxima is almost as easy as giving a list with both x and y parameters to the Plot2D function. For example, to plot a circle,

(%i1) plot2d( [parametric, cos(t), sin(t)], [t,0,2*%pi] );

To make the plot smoother, specify more ticks.

(%i2) plot2d( [parametric, cos(t), sin(t)], [t,0,2*%pi], ['nticks,100] );

Axiom

Plotting a parameterized curve in Axiom is almost as easy as giving ordered pairs with the x and y parameters to the draw() function. For example, to plot a circle,

(2) -> draw( curve(cos(t),sin(t)), t=0..2*%pi )

Octave

Plotting a parameterized curve in Octave is trivial. In effect, the regular plot() function already gives a plot of a parametrized function. It’s just that using x as the parameter is the most typical use. However, nothing stops us from using it in a more general fashion. For example, to plot a circle:

octave:1> t = [0 : .1 : 2*pi ];
octave:2> x = cos(t);
octave:3> y = sin(t);
octave:4> plot(x,y)

The circle may appear out of shape if the scales of the axes aren’t drawn the same (resizing the window will correct the shape). You can ask gnuplot to draw the axes with precisely the same scales; "ratio -1" is a special setting for gnuplot that makes sure the y-unit and the x-unit have the same size.

octave:5> gset size ratio -1
octave:6> replot

On newer versions of Octave, use this instead:

octave: 5> axis equal

Unit circle plotted with Octave

Plotting functions of two variables (3D plotting)

Mathematica

In[8]:= Plot3D[ x^2 + 3*y^2, {x, -5, 5}, {y, -5, 5} ]

If you would like to interactively rotate the image, older version of Mathematica first need the RealTime3D system. Newer versions of Mathematica already have interactive image manipulation.

In[1]:= <<RealTime3D`
In[2]:= Plot3D[ x^2 + 3*y^2, {x, -5, 5}, {y, -5, 5} ];

The default setting (i.e. no interactive 3D) can be restored via:

In[3]:= <<Default3D`

Maxima

(%i1) plot3d( x^2 + 3*y^2, [x, -5, 5], [y, -5, 5], [grid,20,20] );

There are many attributes you can manipulate on the plot including contour lines and axis label. See Contour plots with Maxima for details.

In current versions of Maxima, the image can be manipulated interactively with the mouse. If you still have an older version of Maxima for Linux and would like to interactively rotate the image, you can specify mgnuplot to generate the plot.

(%i2) plot3d( x^2 + 3*y^2, [x, -5, 5], [y, -5, 5], [grid,20,20], [plot_format,mgnuplot] );

Axiom

The same draw() command that does 2D plotting does 3D plotting as well. Simply give a third argument with the bounds for the second variable.

(3) -> draw( x**2 + 3*y**2, x=-5..5, y=-5..5 )

Octave

As with 2D plotting, Octave needs a set of points to plot. In this case, there is a function called meshgrid() which can generate them. You pass it an array of the grid-values in the x-direction and the y-direction and it returns two matrices, xx and yy, that contain the coordinates for an entire rectangular grid. Once you have evaluated your function on those points, the mesh() function displays the graph.

octave:12> function z = f(x,y)
> z = x.^2 + 3*y.^2;
> endfunction
octave:13> x = [ -5 : 0.25 : 5 ];
octave:14> y = x;
octave:14> [ xx, yy ] = meshgrid( x, y );
octave:15> zz = f( xx, yy );
octave:16> mesh( xx, yy, zz );

The following implementation is equivalent but shorter:

octave:1> x = y = [-5 : 0.25 : 5];
octave:2> [xx, yy] = meshgrid (x, y);
octave:3> mesh (xx, yy, xx.^2 + 3*yy.^2);

Paraboloid plotted with Octave.

To get a different view use "gset view".

octave:17> gshow view
        view is 60 rot_x, 30 rot_z, 1 scale, 1 scale_z
octave:18> gset view 45, 45
octave:19> replot

For kicks you might try this animation:

octave:20> for i = [0:10:360]
> eval ( sprintf( "gset view 45, %4.1f", i ))
> replot
> endfor

The eval/sprintf construct is required because the parameters passed to gset are not evaluated (so if you put "gset view 45, i" it would complain that "i" was not a number).

3D plots over general regions

Mathematica

We can plot a function like f(x,y) = x^2+y^2 over a general region, like the area enclosed by the curves y=x and y=x^2, by creating a function defined only on our region of interest and plotting that.

In[1]:= f[x_,y_] := x^2 + y^2 /; y<=x && y>=x^2

In[2]:= Plot3D[ f[x,y], {x,0,1}, {y,0,1} ]

Maxima

We can plot a function like f(x,y) = x^2+y^2 over a general region, like the area enclosed by the curves y=x and y=x^2, by creating a piecewise-defined function (defined to be 0 off our region of interest) and plotting that.

(%i1) f(x,y) := if y<=x and y>=x^2 then x^2+x^2 else 0;
                                            2       2    2
(%o1)        f(x, y) := if y <= x and y >= x  then x  + x  else 0
(%i2) plot3d( f(x,y), [x,0,1], [y,0,1] );

Note: Older versions of Maxima will give an error on this plot. You must tell Maxima not to evaluate the function too early, by enclosing it inside '( ) as in:

(%i3) plot3d( '( f(x,y) ), [x,0,1], [y,0,1] );

Octave

We can plot a function like f(x,y) = x^2+y^2 over a general region, like the area enclosed by the curves y=x and y=x^2, by creating a piecewise-defined function (defined to be 0 off our region of interest) and plotting that.

octave:1> function z = f(x,y)
> z = (y<=x) .* (y>=x.^2) .* (x.^2 + y.^2);
> endfunction
octave:2> x = y = [ 0 : .02 : 1 ];
octave:3> [xx,yy] = meshgrid(x,y);
octave:4> zz = f(xx,yy);
octave:5> mesh(xx,yy,zz)

Function defined on a general domain

Drawing contour plots

Mathematica

In[11]:= ContourPlot[ x^2 - y^2, {x, -5, 5}, {y, -5, 5} ]

Maxima

Newer versions of Maxima can generate contour plots with the contour_plot() function.

(%i1) contour_plot( x^2 - y^2, [x, -5, 5], [y, -5, 5] );

Since plotting in Maxima is done by Gnuplot, a contour plot may be manipulated by passing values through to the plotting engine. For example, the number of contour lines may be changed.

(%i2) contour_plot( x^2 - y^2, [x, -5, 5], [y, -5, 5], [gnuplot_preamble, "set cntrparam levels 12"] );

Another really dynamic way to visualize contour lines is by drawing them right on a surface. This is also done by passing arguments through to gnuplot, but using the usual plot3d() function.

(%i3) plot3d(  x^2 - y^2, [x, -5, 5], [y, -5, 5], [gnuplot_preamble, "set contour surface" ] );

Perhaps it is easier to see what is happening with the axes labeled. We can easily include that as well.

(%i4) plot3d(  x^2 - y^2, [x, -5, 5], [y, -5, 5],
 [gnuplot_preamble, "set contour surface; set xlabel 'x axis'; set ylabel 'y axis'; set zlabel 'z axis'" ] );

You may add as many hints to gnuplot as you wish. Gnuplot options/commands you may be interested in trying:

  • set contour base

  • set contour surface

  • set contour both

  • set cntrparam levels 10

  • set cntrparam levels discrete 1,2,4,8 (set contour lines at specific values)

  • set hidden3d (makes the surface opaque)

  • set nohidden3d (makes the surface transparent)

  • set pm3d (color the surface according to height)

  • set surface (draw the surface)

  • set nosurface (hide the surface, but contour lines and pm3d will still show)

  • set xlabel \'x axis\'

  • set ylabel \'y axis\'

  • set zlabel \'z axis\'

  • set view 45,115 (rotate the viewpoint around x axis and z axis)

Older versions of Maxima

Older versions of Maxima were lacking the ability to natively create contour plots. On Linux systems we could add them; it merely required changing some of the mgnuplot parameters after the graph is displayed. Begin by specifying an mgnuplot formatted plot.

(%i1) plot3d( x^2-y^2, [x,-5,5], [y,-5,5], [plot_format,mgnuplot] );

When the graph is plotted, there is a companion window called mgnuplot, and it contains a text field called Edit:. You can type commands in this field to effect changes on the graph. For example to get a contour plot added to the graph,

Edit: set nohidden3d
Edit: set contour surface

You have some control over how contour lines appear, including set contour base, set contour surface, and set contour both. To make the lines go away, use set nocontour.

If you want to focus on the contour lines themselves, you can now rotate the grid until xrotation=0 and zrotation=0, or change the view manually. You can also modify the number of contour lines visible or specify contour lines at particular heights of interest. One interesting view is where xrotation=90 (so all of the contour lines are horizontal).

Edit: set view 0,0
Edit: set nosurface
Edit: set cntrparam levels 10
Edit: set cntrparam levels discrete 1,2,4,8

Edit: set xlabel "x-axis"
Edit: set ylabel "y-axis"
Edit: set format z {}

Using the PM3D mode, the surface becomes solid and is colored according to height; this is similar to a contour map. PM3D mode makes surface rendering redundant, so we can remove the surface component to make the graph render more quickly.

Edit: set pm3d
Edit: set nosurface

Octave

Because Octave uses Gnuplot to display its graphics, it can take advantage of Gnuplot’s ability to create contour lines, just as Maxima can. There is even a pre-built command called contour that automates some of this. It takes as arguments: a matrix of z-values, the number of contours to draw, a vector of x-coordinates, and a vector of y-coordinates.

octave:1> function z = f(x,y)
> z = x.^2 - y.^2;
> endfunction
octave:2> x = [ -5 : 0.25 : 5 ];
octave:3> y = x;
octave:4> [ xx, yy ] = meshgrid( x, y );
octave:5> zz = f( xx, yy );
octave:6> contour( zz, 5, x, y );

Notice that the setup is much the same as for 3D graphing, and so you can start from a 3D plot and adjust the rest of the settings in any way you like. Continuing the calculations from above,

octave:7> mesh( xx, yy, zz );

octave:8> gset nohidden
octave:9> gset contour surface
octave:10> replot

octave:11> gset view 0,0
octave:12> gset nosurface
octave:13> gset cntrparam levels 10
octave:14> replot

octave:15> gset cntrparam levels discrete 1,2,4,8
octave:16> replot

octave:17> gset xlabel "x-axis"
octave:18> gset ylabel "y-axis"
octave:19> gset format z ""
octave:20> replot

Alternative Method

Because Octave is FreeSoftware, we are free to modify it if we wish. For technical reasons, it is awkward that Octave draws contour lines by manipulating a 3D graph object. For one thing, it means that we are not free to draw other 2D objects on the same graph.

To make a real 2D contour plot (on a Linux system) download contour-2.1.tar.gz. This file contains source code for a new contour plot function for Octave 2.1. If you are still running version 2.0, you can use contour.tar.gz. To build it,

$ tar xzf contour-2.1.tar.gz
$ cd contour-2.1
$ make
mkoctfile __cntr__.cc

When you start Octave from this directory, you now have access to a new contour plot function.

$ octave
octave:1> help contour
contour is the function defined from: /home/dbindner/octave/contour.m

 contour (x,y,z,levels): plot contour lines of a function

  Plot contour lines by interpolating the values of a function
  on a grid.  If x,y,z are matrices with the same dimensions
  the the corresponding elements represent points on the function.
  If levels is a scalar, it specifies a number of contour lines
  to draw at evenly spaced heights.  It can also be a vector of
  specific heights at which to draw level curves.

  If x is a vector and y is a vector, then z must be a matrix
  with the same number of rows as x and the same number of
  columns as y.  The corresponding points from which the contour
  lines are interpolated are x(i),y(j),z(i,y).


Additional help for builtin functions, operators, and variables
is available in the on-line version of the manual.  Use the command
`help -i <topic>' to search the manual index.

Help and information about Octave is also available on the WWW
at http://www.che.wisc.edu/octave/octave.html and via the
help-octave@bevo.che.wisc.edu mailing list.

Note that the arguments are are changed from the original. They match the mesh() function. Now a simple variation of the previous directions will generate a beautiful contour plot.

octave:1> function z = f(x,y)
> z = x.^2 - y.^2;
> endfunction
octave:2> x = [ -5 : 0.25 : 5 ];
octave:3> y = x;
octave:4> [ xx, yy ] = meshgrid( x, y );
octave:5> zz = f( xx, yy );
octave:6> contour( xx, yy, zz, 5 );

One technical advantage of using this method to draw contour lines is that you can add other 2D drawings to a contour plot. That makes it possible to do things like this exploration lab of the gradient vector: pdf/ps

Plotting a surface and its tangent plane

Mathematica

Plotting a surface together with its tangent plane is a matter of generating two plots and displaying them together with the Show command.

For example, the surface z = x^2 - y^2 at the point (1,2,-3) has the tangent plane z+3 = 2(x-1) -4(x-2). We can verify this by calculating the partial derivatives using Mathematica.

In[1]:= f[x_, y_] = x^2 - y^2
Out[1]:= x^2 - y^2
In[2]:= fx[x_, y_] = D[ f[x,y], x ]
Out[2]:= 2x
In[3]:= fy[x_, y_] = D[ f[x,y], y ]
Out[3]:= -2y
In[4]: { fx[1,2], fy[1,2] }
Out[4]: {2, -4}

Generate a plot of each function using the Plot3D command. Here, we we use the argument DisplayFunction->Identity because we don’t care to see the individual graphs. We’ll define the surface on a square domain from -3 to 3 but the tangent plane will only exist around the point (1,2,-3). This will make the plane appear as a small flat patch against the surface.

In[5]:= surface = Plot3D[ x^2-y^2, {x,-3,3}, {y,-3,3}, DisplayFunction->Identity ];
In[6]:= plane = Plot3D[ -3 + 2*(x-1) - 4*(y-2), {x,0.5,1.5}, {y,1.5,2.5}, DisplayFunction->Identity ];

Now we show both plots together, using the Show function. I am uncertain precisely what the DisplayFunction part means, but it is necessary to make the graph appear. Unfortunately, the view is bad, so a custom ViewPoint also helps.

In[7]:= Show[ surface, plane, DisplayFunction->$DisplayFunction];
In[8]:= Show[ surface, plane, DisplayFunction->$DisplayFunction, ViewPoint->{5,5,3}, AxesLabel->{x,y,z} ];

Octave

Plotting a surface together with its tangent plane is only a small adaptation of the 3D plotting directions given above. The method is to plot the surface as usual, give the "hold on" command to preserve the surface, and then plot the plane.

For example, the surface z = x^2 - y^2 at the point (1,2,-3) has the tangent plane z+3 = 2(x-1) -4(x-2). To get a view of the surface and tangent together, first plot the surface.

octave:1> function z = f(x,y); z = x.^2 - y.^2; endfunction
octave:2> x = y = [-3 : .2 : 3 ];
octave:3> [xx,yy] = meshgrid(x,y);
octave:4> zz = f(xx,yy);
octave:5> mesh(xx,yy,zz)

To make it easier to spot the tangent plane, we’ll only define it nearby the point (1,2,-3), not on the entire domain. Notice how below we set a smaller x domain of (0.5,1.5) and a smaller y domain of (1.5,2.5). This will make the tangent plane appear as a small patch against the curved surface.

octave:6> function z = t(x,y); z = -3 + 2*(x-1) - 4*(y-2); endfunction
octave:7> x1 = [ 0.5 : .1 : 1.5 ]; y1 = [ 1.5 : .1 : 2.5 ];
octave:8> [xx1,yy1] = meshgrid(x1,y1);
octave:9> zz1 = t(xx1,yy1);

Here we use "hold on" to keep the surface visible while we add the plane.

octave:10> hold on
octave:11> mesh(xx1,yy1,zz1)

Rotate the surface however you like and replot to get a good view:

octave:12> for i = [30:5:210]; eval ( sprintf( "gset view 75, %4.1f", i )); replot; endfor
octave:13> gset view 85,210; replot
octave:14> gset view 120,245; replot

Perhaps some transparency will help:

octave:15> gset nohidden3d
octave:16> for i = [30:1:300]; eval ( sprintf( "gset view 90, %4.1f", i )); replot; endfor

Plotting a parametric surface in 3-space

Mathematica

The ParametricPlot3D[] function can plot parametric curves and parametric surfaces in 3-space.

In[1]:= ParametricPlot3D[ { 2*(Cos[v] + u*Cos[v/2]*Con[v]), 2*(Sin[v] + u*Cos[v/2]*Sinv[v]), 2*u*Sin[v/2] }, {u,-0.4,0.4}, {v,0,2*Pi} ]

Maxima

When the first argument of plot3d() is a vector, Maxima performs a parametric plot. The vector argument can be a vector-valued function as shown here, or an explicit vector.

(%i1) r(u,v) := [ 2*(cos(v) + u*cos(v/2)*cos(v)), 2*(sin(v) + u*cos(v/2)*sin(v)), 2*u*sin(v/2) ];

                                   v                             v
(%o1) r(u, v) := [2 (COS(v) + u COS(-) COS(v)), 2 (SIN(v) + u COS(-) SIN(v)),
                                   2                             2

                                                                            v
                                                                    2 u SIN(-)]
                                                                            2
(%i2) plot3d(r(u,v), [u,-0.4,0.4], [v,0,2*%pi] );

Octave

Plotting in Octave is simply a matter of generating (x,y,z) coordinates for the mesh() function. If a surface is parametrized by <x,y,z> = <2*(cos(v) + u*cos(v/2)*cos(v)), 2*(sin(v) + u*cos(v/2)*sin(v)), 2*u*sin(v/2)> then this is how it would be drawn in Octave:

octave:1> u = [-0.4 : 0.1 : 0.4 ]; v = [0 : 0.1 : 2*pi ];
octave:2> [uu,vv] = meshgrid(u,v);
octave:3> xx = 2*( cos(vv) + uu.*cos(vv/2).*cos(vv) );
octave:4> yy = 2*( sin(vv) + uu.*cos(vv/2).*sin(vv) );
octave:5> zz = 2.*uu.*sin(vv/2);
octave:6> mesh(xx,yy,zz)

Axiom

In Axiom, a parameterized surface is drawn using the same draw() function as other kinds of plots. It is almost as simple as passing the parametrizations straight through: For <x,y,z> = <2*(cos(v) + u*cos(v/2)*cos(v)), 2*(sin(v) + u*cos(v/2)*sin(v)), 2*u*sin(v/2)> the plot would be:

(1) -> draw( surface( 2*(cos(v) + u*cos(v/2)*cos(v)), 2*(sin(v) + u*cos(v/2)*sin(v)), 2*u*sin(v/2) ), u=-.4...4, v=0..2*%pi )

Plotting a parametrized curve in 3-space

Mathematica

Say you have z(x,y) = x^2*y + 3*x*y^4 where x = x(t) = sin(2*t) and y = y(t) = cos(t). Then effectively, z is a function of t and we can calculate dz/dt. But what does this mean? Well, the vector <x(t),y(t),z(t)> parametrizes a curve in 3-space, and dz/dt is the slope as you travel around via the paramentrization (as opposed, say, to in the x-direction or y-direction).

We can get a view of this curve using the ParametricPlot3D function of Mathematica.

In[1]:= x[t_] := Sin[ 2*t];
In[2]:= y[t_] := Cos[t];
In[3]:= z[x_, y_] := x^2*y  + 3*x*y^4;
In[4]:= ParametricPlot3D[ {x[t], y[t], z[x[t], y[t]] }, {t, 0, 2*Pi} ]

Maxima

Say you have z(x,y) = x^2*y + 3*x*y^4 where x = x(t) = sin(2*t) and y = y(t) = cos(t). Then effectively, z is a function of t and we can calculate dz/dt. But what does this mean? Well, the vector <x(t),y(t),z(t)> parametrizes a curve in 3-space, and dz/dt is the slope as you travel around via the paramentrization (as opposed, say, to in the x-direction or y-direction).

So, how can we get a view of this curve? We use the parametric plotting capability of Maxima, by giving a list of functions [x,y,z] as the argument to plot. We also have to cheat a bit. Maxima expects two domain variables to be defined. So we define a real variable t and a dummy variable u, which we never use. For efficiency, we can declare the number of grid points to use for each variable, and we condemn u to use only 1.

(%i1) x(t) := sin(2*t);

(%o1)                           x(t) := SIN(2 t)
(%i2) y(t) := cos(t);

(%o2)                            y(t) := COS(t)
(%i3) z(x,y) := x^2*y + 3*x*y^4;

                                       2          4
(%o3)                       z(x, y) := x  y + 3 x y
(%i4) plot3d( [x(t),y(t),z(x(t),y(t))], [t,0,2*%pi], [u,0,1], [grid,60,1], [plot_format, mgnuplot ] );

Axiom

(2) -> x(t) == sin(2*t)
                                                                   Type: Void
(3) -> y(t) == cos(t)
                                                                   Type: Void
(4) -> z(x,y) == x**2*y + 3*x*y**4
                                                                   Type: Void
(5) -> draw( curve( x(t), y(t), z(x(t),y(t)) ), t=0..2*%pi )

Octave

Say you have z(x,y) = x^2*y + 3*x*y^4 where x = x(t) = sin(2*t) and y = y(t) = cos(t). Then effectively, z is a function of t and we can calculate dz/dt. But what does this mean? Well, the vector <x(t),y(t),z(t)> parametrizes a curve in 3-space, and dz/dt is the slope as you travel around via the paramentrization (as opposed, say, to in the x-direction or y-direction).

So, how can we get a view of this curve? Essentially, you need only get the (x,y,z) coordinates together and the mesh() function can plot it. First the functions need to be defined.

octave:1> function r = z(x,y); r = x.^2.*y + 3*x.*y.^4; endfunction
octave:2> function r = x(t); r = sin(2*t); endfunction;
octave:3> function r = y(t); r = cos(t); endfunction

There’s only one catch after that. The mesh() function behaves differently when it receives vectors for arguments than when it receives matrices. We really want the matrix behavior, so we’ll double the (single) row of a vector of time values t and call the matrix tt. That will ensure that xx, yy, and zz defined from it are all matrices with two rows. Essentially, we’ll ask Octave to plot the curve twice, but no harm comes from doing that.

octave:4> t = [ 0:.1:2*pi ];
octave:5> tt = [ t ; t ];

From tt, we define all of the points of the parametrized curve and plot them with mesh().

octave:6> xx = x(tt);
octave:7> yy = y(tt);
octave:8> zz = z(xx,yy);
octave:9> mesh(xx,yy,zz)

Finally, the graph may be rotated like any other 3D graph. For example, borrowing an idea from above:

octave:10> for i = [30:1:300]; eval ( sprintf( "gset view 75, %4.1f", i )); replot; endfor

2D Vector Fields

Mathematica

You have to load Graphics`PlotField` before you create vector field plots. After that, to plot the vector field F(x,y) = <1,-1> on the domain [0,10]x[0,10],

In[1]:= <<Graphics`PlotField`
In[2]:= PlotVectorField[ {1,-1}, {x,0,10}, {y,0,10} ]

To plot the vector field F(x,y) = <-y,x> on [-3,3]x[-3,3],

In[3]:= PlotVectorField[ {-y,x}, {x,-3,3}, {y,-3,3} ]

Maxima

You have to load draw before you create vector field plots, and you’ll also need to create a quiver function.

(%i1) load(draw)$

(%i2) quiver( p,q, A,B, scale ) := block(
 [ P,Q, c1, c2, grid_d, points, u, long1, long2, v ],
 P : lambda( [x,y], ev(p)),
 Q : lambda( [x,y], ev(q)),
 c1 : setify( makelist( A[1] + (A[2]-A[1])*i/10, i, 0, 10 )),
 c2 : setify( makelist( B[1] + (B[2]-B[1])*i/10, i, 0, 10 )),
 points : listify( cartesian_product( c1, c2 )),
 grid_d : sqrt((A[2]-A[1])^2 + (B[2]-B[1])^2)/10.0,
 long1 : apply( max, makelist( abs(P(k[1],k[2])), k, points )) / (A[2]-A[1]) * 10,
 long2 : apply( max, makelist( abs(Q(k[1],k[2])), k, points )) / (B[2]-B[1]) * 10,
 u : max(long1, long2),
 if scale#0 and u>grid_d/50.0 then u:scale/u else u:1,
 v : makelist( vector(k, u*[P(k[1],k[2]),Q(k[1],k[2])] ), k, points ),
 apply( draw2d, append([color=blue],[line_width=1],[head_type='nofilled],[head_length=grid_d/8], v ))
)$

After that, to plot the vector field F(x,y) = <1,-1> on the domain [0,10]x[0,10], you call the quiver function with the components (defined in terms of x and y) and coordinate ranges. The last argument controls scaling. If it is 0, you’ll get an unscaled plot, but this is sometimes visually overwhelming. If you pass a 1, the vectors will be auto-scaled to prevent overlapping. You can use numbers larger or smaller than 1 to stretch and shrink the auto-scaled arrows.

(%i3) quiver( 1,-1, [0,10],[0,10], 1 )$

To plot the vector field F(x,y) = <-y,x> on [-3,3]x[-2,2],

(%i4) quiver( -y,x, [-3,3],[-2,2], 1 )$

Alternate solution: If you prefer Mathematica-like syntax, then load the draw module, and use the following plot_vector_field function:

(%i1) load(draw)$

(%i2) plot_vector_field( F, X, Y, scale ) := block(
 [vars, G,P,Q, c1,c2, points, grid_d,long1,long2,u, v],
 vars : [ X[1],Y[1] ],
 P : G( vars, ev(F[1])), P : subst( lambda, G, P ),
 Q : G( vars, ev(F[2])), Q : subst( lambda, G, Q ),
 c1 : setify( makelist(  X[2] + (X[3]-X[2])*i/10, i, 0, 10 )),
 c2 : setify( makelist(  Y[2] + (Y[3]-Y[2])*i/10, i, 0, 10 )),
 points : listify( cartesian_product( c1, c2 )),
 grid_d : sqrt((X[3]-X[2])^2 + (Y[3]-Y[2])^2)/10.0,
 long1 : apply( max, makelist( abs(P(k[1],k[2])), k, points )) / (X[3]-X[2]) * 10,
 long2 : apply( max, makelist( abs(Q(k[1],k[2])), k, points )) / (Y[3]-Y[2]) * 10,
 u : max(long1, long2),
 if scale#0 and u>grid_d/50.0 then u:scale/u else u:1,
 v : makelist( vector(k, u*[P(k[1],k[2]),Q(k[1],k[2])] ), k, points ),
 apply( draw2d, append([color=blue],[line_width=1],[head_type='nofilled],[head_length=grid_d/8], v ))
)$

After that, to plot the vector field F(x,y) = <1,-1> on the domain [0,10]x[0,10], you call the plot_vector_field function with the vector function and coordinate ranges. The last argument controls scaling. If it is 0, you’ll get an unscaled plot, but this is sometimes visually overwhelming. If you pass a 1, the vectors will be auto-scaled to prevent overlapping. You can use numbers larger or smaller than 1 to stretch and shrink the auto-scaled arrows.

(%i3) plot_vector_field( [1,-1], [x,0,10],[y,0,10], 1 )$

To plot the vector field F(x,y) = <-y,x> on [-3,3]x[-2,2],

(%i4) plot_vector_field( [-y,x], [x,-3,3],[y,-2,2], 1 )$

Octave

Using the small m-file quiver.m (save it in your working directory before you start octave), you can create 2D plots of vector fields. The quiver function accepts 4 arguments, (x,y) coordinates of the domain of the vector field, and the (u,v) coordinates of the vectors.

For example, to plot the vector field F(x,y) = <1,-1> on the domain [0,10]x[0,10],

octave:1> [xx,yy] = meshgrid( 0:2:10, 0:2:10 );
octave:2> uu = 0*xx+1; vv = 0*xx-1;
octave:3> quiver(xx,yy, uu,vv)

To plot the vector field F(x,y) = <-y,x> on [-3,3]x[-3,3],

octave:4> [xx,yy] = meshgrid( -3:3, -3:3 );
octave:5> quiver(xx,yy, -yy,xx)

3D Vector Fields

Mathematica

You have to load Graphics`PlotField3D` before you create 3D vector field plots. You’ll probably also want to load RealTime3D` so you can interactively rotate your plots (static 3D vector field plots can be difficult to decipher).

To plot the vectorfield F(x,y,z) = <-y,x,z>,

In[1]:= <<Graphics`PlotField3D`
In[2]:= <<RealTime3D`
In[3]:= PlotVectorField3D[ {-y,x,z}, {x,-3,3}, {y,-3,3}, {z,-3,3} ]

Maxima

You have to load draw before you create vector field plots, and you’ll also need to create a quiver3 function.

(%i1) load(draw)$

(%i2) quiver3( p,q,r, A,B,C, scale ) := block(
 [ P,Q,R, c1,c2,c3, grid_d, points, u, long1,long2,long3, v ],
 P : lambda( [x,y,z], ev(p)),
 Q : lambda( [x,y,z], ev(q)),
 R : lambda( [x,y,z], ev(r)),
 c1 : setify( makelist( A[1] + (A[2]-A[1])*i/5, i, 0, 5 )),
 c2 : setify( makelist( B[1] + (B[2]-B[1])*i/5, i, 0, 5 )),
 c3 : setify( makelist( C[1] + (C[2]-C[1])*i/5, i, 0, 5 )),
 points : listify( cartesian_product( c1, c2, c3 )),
 grid_d : sqrt((A[2]-A[1])^2 + (B[2]-B[1])^2 + (C[2]-C[1])^2)/5.0,
 long1 : apply( max, makelist( abs(P(k[1],k[2],k[3])), k, points )) / (A[2]-A[1]) * 5.0,
 long2 : apply( max, makelist( abs(Q(k[1],k[2],k[3])), k, points )) / (B[2]-B[1]) * 5.0,
 long3 : apply( max, makelist( abs(R(k[1],k[2],k[3])), k, points )) / (C[2]-C[1]) * 5.0,
 u : max( long1, long2, long3),
 if scale#0 and u>grid_d/50.0 then u:scale/u else u:1,
 v : makelist( vector(k, u*[P(k[1],k[2],k[3]),Q(k[1],k[2],k[3]),R(k[1],k[2],k[3])] ), k, points ),
 apply( draw3d, append([color=blue],[line_width=1],[head_type='nofilled],[head_length=grid_d/8], v ))
)$

After that, to plot the vector field F(x,y,z) = <-y,x,z> on the domain [-2,2]x[-3,3]x[-4,4], you call the quiver3 function with the components (defined in terms of x,y,and z) and coordinate ranges. The last argument controls scaling. If it is 0, you’ll get an unscaled plot, but this is sometimes visually overwhelming. If you pass a 1, the vectors will be auto-scaled to prevent overlapping. You can use numbers larger or smaller than 1 to stretch and shrink the auto-scaled arrows.

(%i3) quiver3( -y,x,z, [-2,2],[-3,3],[-4,4], 1 )$

Alternate solution: If you prefer Mathematica-like syntax, then load the draw module, and use the following plot_vector_field3d function:

(%i1) load(draw)$

(%i2) plot_vector_field3d( F, X, Y, Z, scale ) := block(
 [vars, G,P,Q,R, c1,c2,c3, points, grid_d,long1,long2,long3,u, v],
 vars : [ X[1],Y[1],Z[1] ],
 P : G( vars, ev(F[1])), P : subst( lambda, G, P ),
 Q : G( vars, ev(F[2])), Q : subst( lambda, G, Q ),
 R : G( vars, ev(F[3])), R : subst( lambda, G, R ),
 c1 : setify( makelist(  X[2] + (X[3]-X[2])*i/5, i, 0, 5 )),
 c2 : setify( makelist(  Y[2] + (Y[3]-Y[2])*i/5, i, 0, 5 )),
 c3 : setify( makelist(  Z[2] + (Z[3]-Z[2])*i/5, i, 0, 5 )),
 points : listify( cartesian_product( c1, c2, c3 )),
 grid_d : sqrt((X[3]-X[2])^2 + (Y[3]-Y[2])^2 + (Z[3]-Z[2])^2)/5.0,
 long1 : apply( max, makelist( abs(P(k[1],k[2],k[3])), k, points )) / (X[3]-X[2]) * 5,
 long2 : apply( max, makelist( abs(Q(k[1],k[2],k[3])), k, points )) / (Y[3]-Y[2]) * 5,
 long3 : apply( max, makelist( abs(R(k[1],k[2],k[3])), k, points )) / (Y[3]-Y[2]) * 5,
 u : max(long1, long2, long3),
 if scale#0 and u>grid_d/50.0 then u:scale/u else u:1,
 v : makelist( vector(k, u*[P(k[1],k[2],k[3]),Q(k[1],k[2],k[3]),R(k[1],k[2],k[3])]), k, points ),
 apply( draw3d, append([color=blue],[line_width=1],[head_type='nofilled],[head_length=grid_d/8], v ))
)$

After that, to plot the vector field F(x,y,z) = <-y,x,z> on the domain [-2,2]x[-3,3]x[-4,4], you call the plot_vector_field3d function with the vector function and coordinate ranges. The last argument controls scaling. If it is 0, you’ll get an unscaled plot, but this is sometimes visually overwhelming. If you pass a 1, the vectors will be auto-scaled to prevent overlapping. You can use numbers larger or smaller than 1 to stretch and shrink the auto-scaled arrows.

(%i3) plot_vector_field3d( [-y,x,z], [x,-2,2],[y,-3,3],[z,-4,4], 1 )$

Octave

Octave (as it is installed on ice) doesn’t quite have what it needs to do plots of 3D vector fields. The most important thing that is missing is a newer version of gnuplot. I’ve installed the development version of gnuplot in my home account. You can tell octave to use it by editing your .octaverc file and inserting the following line.

gnuplot_binary = "/home/ldap/dbindner/usr/bin/gnuplot";

The second thing you’ll need is an updated meshgrid() function, which can generate 3D meshgrids. I have one in meshgrid.m (save a copy in your working directory to use it). Then, using the small m-file quiver3.m, you can create 3D plots of vector fields. The quiver3() function accepts 6 arguments, (x,y,z) coordinates of the domain of the vector field, and the (u,v,w) coordinates of the vectors.

To plot the vectorfield F(x,y,z) = <-y,x,z>,

octave:1> [xx,yy,zz] = meshgrid(-3:3);
octave:2> quiver3(xx,yy,zz, -yy,xx,zz)

Even with the help of a computer, 3D vector fields can be hard to visualize. Often the arrows are long and overlapping. So quiver3() automatically scales the arrows. Add an optional argument to turn off autoscaling (0) or to adjust the arrow length. Set it to a value between 0 and 1 to shorten the arrows or to a value greater than 1 to lengthen them.

octave:3> quiver3(xx,yy,zz, -yy,xx,zz, 0.3)

Printing a plot

Maxima

Maxima doesn’t have a commmand for printing a plot, but it does have options for saving a plot to a file, which you can then print. The most useful file formats are probably Postscript and png (and perhaps dumb, which saves your plot as ASCII art). Since maxima uses gnuplot for its plotting, we simply have Maxima tell gnuplot how to handle our plot.

The first command here saves our plot as a Postscript file called maxplot.ps and the second saves a png file maxplot.png:

(%i1) plot2d( x^2, [x,0,3], [gnuplot_term, ps] );
(%o1)
(%i2) plot2d( x^2, [x,0,3], [gnuplot_term, png] );
(%o2)

We can also specify a file name if we wish:

(%i3) plot2d( x^2, [x,0,3], [gnuplot_term, ps], [gnuplot_out_file, "parabola.eps" ] );
(%o3)