Skip to content

Embedding Maxima-generated plots via plot(), a wrapper for Maimxa's plot2d()

In STACK, the plot command has been defined to be a wrapper for Maxima's plot2d command. The wrapper makes sure that an image file is given an appropriate name, file location, and that Maxima returns a URL to the user giving the image. Not all of the features of plot2d are available through plot.

For example,

  1. Try the following in a castext field. {@plot(x^2,[x,-1,1])@}.
  2. You can add a second variable to control the range of the y-axes. plot(x^2,[x,-1,1],[y,0,2]).
  3. To plot many functions in a single image, we need to define a list of expressions. plot([x^2,sin(x)],[x,-1,1]).
  4. A list of functions can be created with Maxima's makelist command (p(k):=x^k,pl:makelist(p(k),k,1,5),plot(pl,[x,-1,1])).


  • Currently STACK (PHP) calls Maxima, this in turn has gnuplot create a basic SVG image on the server and return a URL.
  • By default plots are surrounded by the <div class="stack_plot">. This puts whitespace around a plot, and places the plot in the centre of the screen. To suppress this div use the option [plottags,false].
  • The default in Maxima is to include a legend consisting of a string representation of the plot. In STACK this is turned off by default. To switch it back on, use the command [legend, true]. Any other value of the option legend will respect the original command.

Maxima plot2d() options supported by plot() in STACK

The following plot options are currently supported by STACK. If you would like to expand the range of options available please contact the developers.

[xlabel, ylabel, label, legend, color, style, point_type, nticks, logx, logy, axes, box, plot_realpart, yx_ratio, xtics, ytics, ztic, grid2d, adapt_depth],

Options only available in plot()

Size of images

To change the size of the image use the Maxima variable size, e.g. plot(x^2,[x,-1,1],[size,250,250]).

Image margin

To change the size of the margin around the image use the variable margin, e.g. plot(x^2,[x,-1,1],[margin,5]).

The value of this parameter is used to set gnuplot's margin parameters to the same value X. There is no way to set these individually.

set lmargin X
set rmargin X
set tmargin X
set bmargin X

The margin also contains any axes numbers, labels etc. outside the plot area. A value of [margin, 0] will therefore crop some of the labels.

Alternate text for an image (alt tag)

The default alternate text for an image (img alt tag) generated by a plot command such as


is "STACK auto-generated plot of x^2 with parameters [[x,-2,2]]". If your question asks students to "give an algebraic expression which describes this curve" then you will need to set alternative text which does not include the answer.

To set a specific alt tag, pass an equation [alt,"..."] as an argument to the plot function.

plot(x^2,[x,-2,2],[alt,"Hello world"]);

If you would like an expression as part of this then try

plot(p,[x,-2,2],[alt,sconcat("Here is ",string(p))]);

Language strings

Note, you cannot put language strings directly into the alt-text. E.g. the following will not be translated.

{@plot(x^2,[x,-2,2],[alt,"[[lang code='en,other']]A quadratic curve[[/lang]][[lang code='no']]En kvadratisk kurve[[/lang]]"])@}

You can define a castext element in the question variables which does get translated, e.g.

altlbls: castext("[[lang code='en,other']]A quadratic curve[[/lang]][[lang code='no']]En kvadratisk kurve[[/lang]]");

and then use this in the castext:


This technique can be put into other language dependent plot variables. E.g.

xlabeltrans: castext("[[lang code='en,other']]Independent variable[[/lang]][[lang code='no']]Uavhengig variabel[[/lang]]");
ylabeltrans: castext("[[lang code='en,other']]Dependent variable[[/lang]][[lang code='no']]Avhengig variabel[[/lang]]");

Then in the castext {@plot(x*sin(1/x),[x,-1,2],[xlabel,xlabeltrans],[ylabel,ylabeltrans])@}

Example plots

Traditional axes

A traditional plot with the axes in the middle can be generated by the following.

{@plot([x^2/(1+x^2),2*x/(1+x^2)^2], [x, -2, 2], [y,-2.1,2.1], [box, false], [yx_ratio, 1], [axes, solid], [xtics, -2, 0.5, 2],[ytics, -2, 0.5, 2])@}


The ylabel command rotates its argument through 90 degrees. If you want a horizontal label on the -axis you will need to use the label command instead.

{@plot([x^2/(1+x^2),2*x/(1+x^2)^2], [x, -2, 2], [y,-2.1,2.1], [label,["y",-2.5,0.25]])@}


The grid is controlled by the maxima command grid2d. Compare the following.

{@plot([x^2/(1+x^2),2*x/(1+x^2)^2], [x, -2, 2], [y,-2.1,2.1], grid2d)@}
{@plot([x^2/(1+x^2),2*x/(1+x^2)^2], [x, -2, 2], [y,-2.1,2.1])@}

Piecewise functions

A piecewise function can be defined with if statements.

pg1:if x<x0 then f0 else f1;

With castext


Notice that Maxima draws the discontinuity as a vertical line.

For a discontinuous function, use the unit_step and kron_delta functions, e.g.

pg2(x) := f0*unit_step(x0-x) + f1*unit_step(x-x0) + und*kron_delta(x,x0);

Now use:

{@plot(pg2(x), [x,(x0-5),(x0+5)], [y,-10,10], [legend,false])@}

A further example of a step function:

step_fn(x,x0) := unit_step(x-x0-1/2) - unit_step(x-x0+1/2) + und*kron_delta(x,x0+1/2)+ und*kron_delta(x,x0-1/2);

which can be used with {@plot(p1,[x,-5,5])@}.

For a discontinuous function, with end points, add in discrete plots.

pg2(x) := f0*unit_step(x0-x) + f1*unit_step(x-x0) + und*kron_delta(x,x0);

ps:[style, lines, points, points];
pt:[point_type, circle,bullet,circle];
pc:[color, blue,blue,red];

Now use:

{@plot([pg2(x), [discrete,[[x0,C]]], [discrete,[[x0,limit(pg2(x),x,x0,'minus)],[x0,limit(pg2(x),x,x0,'plus)]]]], [x,(x0-5),(x0+5)], [y,-10,10], ps, pt, pc, [legend,false])@}

Interaction with question blocks

It is possible to create multiple plots using the question blocks features. E.g.

[[foreach n="[1,2,3,4,5,6,7,8]"]]
[[/ foreach]]

To illustrate how the margin option can be used compare the above with

[[foreach n="[1,2,3,4,5,6,7,8]"]]
[[/ foreach]]

Bode plots

Maxima has a very basic package for bode diagrams, try load(bode) in a Maxima session. This is not a supported package, so instead you can create Bode diagrams directly with code such as the following.

/* Define two functions to do the plotting */
bose_gain(f,r):=block([p,w], p:plot(20*log(abs( apply(f,[%i*w]) ))/log(10), [w, r[2],r[3]], [logx]), return(p) );
bose_phase(f,r):=block([p,w], p:plot(  carg(  apply(f,[%i*w]))*180/%pi, [w, r[2],r[3]], [logx]), return(p) );
/* Define a transfer function */

/* Produce the graphs */
gain: bose_gain(H,[w,1/1000,1000]);

A catalogue of plots

The following CASText gives representative examples of the plot2d features supported by STACK's plot command. Cut and paste it into the CASchat script. Beware that these are likely to cause a timeout on the CAS if you try them all at once.

<h3>Basic plot</h3>
The following plot tests the option to explicitly test the alt-text.
{@plot(x^3,[x,-3,3], [alt,"What is this function?"])@}
<h3>Multiple graphs, clips the \(y\) values</h3>
<h3>With and without a grid</h3>
{@plot([x^2/(1+x^2),2*x/(1+x^2)^2], [x, -2, 2], [y,-2.1,2.1], grid2d)@}
{@plot([x^2/(1+x^2),2*x/(1+x^2)^2], [x, -2, 2], [y,-2.1,2.1])@}
<h3>Discrete plots</h3>
Basic discrete plot.
Points: by default the points are too large!
{@plot([discrete,[[0,0], [1,1], [1.5,(1.5)^2]]],[x,-2,2],[style, [points]],[point_type, bullet])@}
{@plot([discrete,[[0,0], [1,1], [1.5,(1.5)^2]]],[x,-2,2],[style, [points, 1]],[point_type, bullet])@}
Notice the size of the points is controlled by the second argument in the list `[points, 1]`.  This is documented in Maxima under "Plot option: style".  A more complicated example is below.
{@plot([[discrete,[[0,0], [1,1], [1.5,(1.5)^2]]],[discrete,[[0,0.1], [0.75,1], [1.25,1.5]]]],[style, [points, 1, red, 1 ], [points, 1.5, blue, 1]])@}
Combination of discrete plots with normal plots.
{@plot([x^2, [discrete,[ [0,0], [1,1], [0,2]]]],[x,-2,2])@}
{@plot([x^2, [discrete,[ [0,0], [1,1], [1.5,(1.5)^2]]]],[x,-2,2],[style, lines, [points, 1]],[point_type, bullet])@}
{@plot([[discrete,[[30,7]]], -0.4*x+19],[x,0,60],[y,0,20],[style, points, lines], [color, red, blue],[point_type, asterisk])@}
{@plot([[discrete,[[10, 0.6], [20, 0.9], [30, 1.1], [40, 1.3], [50, 1.4]]], 2*%pi*sqrt(l/980)], [l,0,50],[style, points, lines], [color, red, blue],[point_type, asterisk])@}
Using different point styles.
{@plot([[discrete, [[10, .6], [20, .9], [30, 1.1],[40, 1.3], [50, 1.4]]],[discrete, [[11, .5], [15, .9], [25, 1.2],[40, 1.3], [50, 1.4]]]],[style, points],[point_type,circle,square],[color,black,green])@}
<h3>Parametric plots</h3>
{@plot([parametric, cos(t), sin(3*t), [t,0,2*%pi]], [nticks, 500])@}
<h3>Setting non-trivial options: labels on the axes and legend</h3>
{@plot(x*sin(1/x),[x,-1,2],[xlabel,"Independent variable"],[ylabel,"Dependent variable"],[legend,"This is a plot"])@}
<h3>Log scale for y-axis, with red colour</h3>
{@plot(exp(3*s),[s, -2, 2],[logy], [color,red])@}
<h3>Turn off the box, grid and the axes</h3>
Default options
{@plot([parametric, (exp(cos(t))-2*cos(4*t)-sin(t/12)^5)*sin(t), (exp(cos(t))-2*cos(4*t)-sin(t/12)^5)*cos(t), [t, -8*%pi, 8*%pi]], [nticks, 100])@}
<tt>[axes, false]</tt>
{@plot([parametric, (exp(cos(t))-2*cos(4*t)-sin(t/12)^5)*sin(t), (exp(cos(t))-2*cos(4*t)-sin(t/12)^5)*cos(t), [t, -8*%pi, 8*%pi]], [nticks, 100], [axes,false])@}
<tt>[box, false]</tt>
{@plot([parametric, (exp(cos(t))-2*cos(4*t)-sin(t/12)^5)*sin(t), (exp(cos(t))-2*cos(4*t)-sin(t/12)^5)*cos(t), [t, -8*%pi, 8*%pi]], [nticks, 100], [box,false])@}
<h3>Putting the axes in the middle</h3>
{@plot([x^2/(1+x^2),2*x/(1+x^2)^2], [x, -2, 2], [y,-2.1,2.1], [box, false], [yx_ratio, 1], [axes, solid], [xtics, -2, 0.5, 2],[ytics, -2, 0.5, 2])@}