Next: , Previous: flowchart, Up: Base modules


7.26 contour

This package draws contour lines. To construct contours for a function f, use

guide[][] contour(real f(real, real), pair a, pair b,
                  real[] c, int nx=ngraph, int ny=nx,
                  interpolate join=operator --);
The contour lines c for the function f are drawn on the rectangle defined by the bottom-left and top-right points a and b. The integers nx and ny define the resolution. The default resolution, ngraph x ngraph (here ngraph defaults to 100), can be increased for greater accuracy. The default interpolation operator is operator -- (linear). Spline interpolation (operator ..) generally produces more accurate pictures, but as usual, can overshoot in certain cases.

To construct contours for an array of data values on a uniform two-dimensional lattice, use

guide[][] contour(real[][] f, real[][] midpoint=new real[][],
                  pair a, pair b, real[] c,
                  interpolate join=operator --);

To construct contours for an array of irregularly spaced points and an array of values f at these points, use one of the routines
guide[][] contour(pair[] z, real[] f, real[] c,
                  interpolate join=operator --);
guide[][] contour(real[] x, real[] y, real[] f, real[] c,
                  interpolate join=operator --);
The contours themselves can be drawn with one of the routines
void draw(picture pic=currentpicture, Label[] L=new Label[],
          guide[][] g, pen p=currentpen)

void draw(picture pic=currentpicture, Label[] L=new Label[],
          guide[][] g, pen[] p)

The following simple example draws the contour at value 1 for the function z=x^2+y^2, which is a unit circle:

import contour;
size(75);

real f(real a, real b) {return a^2+b^2;}
draw(contour(f,(-1,-1),(1,1),new real[] {1}));
contour1.png

The next example draws and labels multiple contours for the function z=x^2-y^2 with the resolution 100 x 100, using a dashed pen for negative contours and a solid pen for positive (and zero) contours:

import contour;
import stats;
size(200);

real f(real x, real y) {return x^2-y^2;}
int n=10;
real[] c = new real[n];
for(int i=0; i < n; ++i) c[i]=(i-n/2)/n;

pen[] p=sequence(new pen(int i) {
    return (c[i] >= 0 ? solid : dashed)+fontsize(6);
  },n);

Label[] Labels=sequence(new Label(int i) {
    return Label(c[i] != 0 ? (string) c[i] : "",Relative(unitrand()),(0,0),
		 UnFill(1bp));
  },n);

draw(Labels,contour(f,(-1,-1),(1,1),c),p);
contour2.png

The next example illustrates how contour lines can be drawn on color density images:

import graph;
import palette;
import contour;

size(10cm,10cm,IgnoreAspect);

pair a=(0,0);
pair b=(2pi,2pi);

real f(real x, real y) {return cos(x)*sin(y);}

int N=200;
int Divs=10;
int divs=2;

defaultpen(1bp);
pen Tickpen=black;
pen tickpen=gray+0.5*linewidth(currentpen);
pen[] Palette=BWRainbow();

scale(false);

bounds range=image(f,Automatic,a,b,N,Palette);
    
xaxis("$x$",BottomTop,LeftTicks,Above);
yaxis("$y$",LeftRight,RightTicks,Above);

// Major contours
real[] Cvals;
Cvals=sequence(11)/10 * (range.max-range.min) + range.min;
draw(contour(f,a,b,Cvals,N,operator ..),Tickpen);

// Minor contours
real[] cvals;
real[] sumarr=sequence(1,divs-1)/divs * (range.max-range.min)/Divs;
for (int ival=0; ival < Cvals.length-1; ++ival)
    cvals.append(Cvals[ival]+sumarr);
draw(contour(f,a,b,cvals,N,operator ..),tickpen);

palette("$f(x,y)$",range,point(NW)+(0,0.5),point(NE)+(0,1),Top,Palette,
	PaletteTicks(N=Divs,n=divs,Tickpen,tickpen));


imagecontour.png

Finally, here is an example that illustrates the construction of contours from irregularly spaced data:

import contour;

size(200);

int n=100;
pair[] points=new pair[n];
real[] values=new real[n];

real r() {return 1.1*(rand()/randMax*2-1);}

for(int i=0; i < n; ++i)
  points[i]=(r(),r());

real f(real a, real b) {return a^2+b^2;}

for(int i=0; i < n; ++i)
  values[i]=f(points[i].x,points[i].y);

draw(contour(points,values,new real[]{0.25,0.5,1},operator ..),blue);
contour3.png

In the above example, the contours of irregularly spaced data are constructed by first creating a triangular mesh from an array z of pairs, using Gilles Dumoulin's C++ port of Paul Bourke's triangulation code:

int[][] triangulate(pair[] z);
size(200);
int np=100;
pair[] points;

real r() {return 1.2*(rand()/randMax*2-1);}

for(int i=0; i < np; ++i)
  points.push((r(),r()));

int[][] trn=triangulate(points);

for(int i=0; i < trn.length; ++i) {
  draw((points[trn[i][0]])--(points[trn[i][1]]));
  draw((points[trn[i][1]])--(points[trn[i][2]]));
  draw((points[trn[i][2]])--(points[trn[i][0]]));
}

for(int i=0; i < np; ++i)
  dot(points[i],red);
triangulate.png

The example Gouraudcontour illustrates how to produce color density images over such irregular triangular meshes.