Geoff Riley's mass writing involvement

I'm an Open University Student studying for a BSc Computing. I live in Lymm, just south of Warrington, in the North West of England with my wife Carole. I spend a lot of time wondering why the world can't be a nicer place: and then I get on with writing a program or producing a website.

Monday 1 June 2009

Chunk 14: The final lump

So, we can draw fancy pictures by using functions, what about something more… practical? Let’s visit our old friend ‘the graph’.

Graphs are made up of a number of distinct elements, these elements may vary in depictions, but they are all basically the same: this means we can generalise them!

The basic elements of a graph are: the axes; the data points; and the labels. We learnt earlier about arrays, these are ideal for holding the values for the data points and labels, and the axes can be automatically calculated to accommodate the given data points.

To start off, we have a number of items that we need to declare globally for controlling our graph:

//Define our point types
int doCIRCLE = 1;
int doSQUARE = 2;
//Define viewport
int vpHEIGHT = 400;
int vpWIDTH = 400;
//Define our margin
int margin = 20;
//Define our scaling factors (calculated in the code)
float xFactor, yFactor;
float xOffset, yOffset;
float xVIEWOFFSET, yVIEWOFFSET;
//Define data point size (how big to draw the squares or circles)
float pSIZE = 5;


And, of course, we’ll need some data points… let’s have two simple straight lines for this demonstration:

//Define our data points
float dp1[] = {1,2,3,4,5,6,7,8,9,10};
float dp2[] = {10,9,8,7,6,5,4,3,2,1};

Drawing the axes is easy, so let’s get that out of the way immediately:

void drawAxes () {
float bottom = vpHEIGHT-margin ;
float right = vpWIDTH-margin ;
// Draw Y axis
line(margin,bottom,margin,margin);
// Draw X axis
line(margin,bottom,right,bottom);
}

Slightly more complicated is the calculating of the factors for the point drawing. Wouldn’t it be nice if we could just have a routine that would take all the data points and do the number crunching away from the main stream of the program? Well the magic of the function argument list allows us to pass arrays as well as single floats or integers… we can have a function that takes the two arrays and sets up the factors before coming back to us. Great.

void calculateFactors(float[] data1, float[] data2) {
// We are looking for minimum and maximum data points.
// This is only set up to expect two sets of data points,
// a truly flexible routine would take an array of arrays!
float minY = min ( min(data1), min(data2) );
float maxY = max ( max(data1), max(data2) );
// Check how many data points we have -- check in both
// arrays just in case they aren't the same!
float count = max ( data1.length, data2.length );
// Set our offsets working from the bottom left corner, so
// that we don't need to be concerned about such things
// during the graph plotting process.
xVIEWOFFSET = margin ;
yVIEWOFFSET = vpHEIGHT - margin ;
// The y range should fit neatly between the top and
// bottom of the graph --- automatically taking care of
// the inversion of the viewport.
// Subtracting maxY from minY gives us a negative factor!
yFACTOR = (float)(vpHEIGHT - margin * 2) / (minY - maxY) ;
// The x range should spread out across the x axis, this
// is simply the width of the graph divided by the number
// of points
xFACTOR = (float)(vpWIDTH - margin * 2) / (count - 1) ;
// The offsets within the drawing area help us to move
// into the area of graph rather than always being zero
// based
xOFFSET = 0 ;
yOFFSET = - minY ;
}

Now that we’ve got the scaling sorted, we can just go ahead and plot our points. That sounds like a cue for another function. The last function that we’ll look at here is one to loop through the data point array and plot them on the graph, again the arguments do the magic by taking an array of floats, and in this case the type of point that we want to be drawn.

void plotPoints(float[] data, int shapeType) {
// Loop through plotting the appropriate points
for ( int i = 0 ; i < style=""> float x = xVIEWOFFSET + ( i + xOFFSET ) * xFACTOR ;
float y = yVIEWOFFSET + ( data[i] + yOFFSET ) * yFACTOR ;
drawShape(shapeType, x, y, pSIZE) ;
}
}

This is a very simple routine for plotting the points, it could be easily expanded to draw lines between the points (this would be done first with so that the points were ‘on top’ of the lines), or to draw histograms, or to include curve fitting and so on. The type of thing that you would not include would be extensive statistical analysis and the like – that belongs in a completely separate function.

Now, bringing all these functions together, we have a comparatively short program that can perform a fairly impressive purpose:

void setup(){
size(vpWIDTH,vpHEIGHT);
background(255);
calculateFactors(dp1,dp2);
drawAxes();
fill(224,0,0);
plotPoints(dp1,doSQUARE);
fill(0,224,0);
plotPoints(dp2,doCIRCLE);
}

As can be seen from the result, this particular pair of graphs is nothing heroic, but if you play with the values in dp1 and dp2, along with the other configuration variables you can find different results.

Modifying the plotPoints function to draw connecting lines, and then giving appropriate data points, it is possible to even draw rudimentary pictures.

It all seems a little fishy to me though.

Saturday 30 May 2009

Chunk 14: Second 800 words....

So have two functions that can draw circles and squares, what if we what one function that is capable of drawing either? Well, this is simply a case of providing an argument that tells the function which type of figure to draw: again, the magic is in the function arguments.

Let us create a new function called ‘drawShape’ then, and have it accept and argument specifying the type of shape followed by the usual position and size arguments that we’ve been using above; we’ll end up with a function like this:

void drawShape( int shapeType, float x, float y, float width ) {
// The code to do the drawing
}

We’ll use ‘width’ as the generic size since it covers the diameter of the circle well also.

We come to a slight stumbling block here though – what do we pass as an argument for ‘shapeType’? The common method for answering this is to define some constant values that are agreed upon and defined globally at the top of the file, so we could have:

int doCIRCLE = 1;
int doSQUARE = 2;

and then our function will be:

void drawShape( int shapeType, float x, float y, float width ) {
if ( shapeType == doCIRCLE ) {
drawCircle(x,y,width);
}
if ( shapeType == doSQUARE ) {
drawSquareCentred(x,y,width);
}
}

If we then modify our setup routine as follows, we find that we get exactly the same result as before.

void setup(){
size(400,400);
background(255);
drawShape(doSQUARE,200,200,150);
drawShape(doCIRCLE,200,200,150);
}

If, at a later date you were to write a function to draw an octagon, then it would be very simple to add that as a further option to the drawShape function by defining a new constant value, eg doOCTAGON. This would then mean that everywhere that you had used drawShape would immediately be able to take advantage of the extra shape without a need to modify the code in each place where it was used.

This is a very powerful aspect of functions and the design of the argument list. It should be noted, however, that a single function should be limited to performing a specific task – if a function is designed to draw a shape, do not then expand it to also calculate some complex mathematical sum on a dataset also, that should be the job for a different function. Sometimes it is all too tempting to add in a bit of extra functionality because ‘it’s doing something else with the same data’, but this generally just leads to difficult to track down bugs and very hard to understand code.

A further useful aspect of functions is their ability to make calls to themselves! This is called ‘recursion’. It is a technique that should generally be avoided, but in controlled situations can be very useful. The reason for its avoidance is that is can very quickly use up a lot of processing memory if the number of recursions is not monitored in some way.

The example we are going to look at uses a ‘depth’ system to keep the recursion in check. This example will show a method of drawing a grid of circles specified by a depth argument… the depth indicates how many multiples of two are applied to the grid along each side, so a depth of 1 would give a 2 by 2 square, a depth of 2 would give a (2 x 2) by (2 x 2) square and so on. We’ll call this function ‘drawCircles’, and you will see when you enter the function the first check is to see if we need to go any deeper before starting to draw: if we do, then we split the area up into quartiles and make a further call for each of the four quartiles with depth reduced by one… this goes on until there’s no need to go any deeper and so a circle is drawn and the function returns either to the original called program or to the next place within the drawCircles function that it had got up to before.

void drawCircles(float x, float y, float diameter, int depth) {
if ( depth > 0 ) {
float halfDiameter = diameter / 2 ;
drawCircles(x - halfDiameter, y - halfDiameter, halfDiameter, depth - 1) ;
drawCircles(x + halfDiameter, y - halfDiameter, halfDiameter, depth - 1) ;
drawCircles(x - halfDiameter, y + halfDiameter, halfDiameter, depth - 1) ;
drawCircles(x + halfDiameter, y + halfDiameter, halfDiameter, depth - 1) ;
}
else {
drawCircle(x,y,diameter);
}
}

It can be seen from this function declaration that drawCircles is called exactly the same as drawCircle, but with the addition of a ‘depth’ argument.

A setup as follows produces the geometric pattern shown:

void setup(){
size(400,400);
background(255);
fill(229,138,19);
drawSquareCentred(200,200,150);
fill(229,19,19);
drawCircles(200,200,150,1);
fill(169,19,229);
drawCircles(200,200,150,2);
fill(56,19,229);
drawCircles(200,200,150,3);
fill(19,229,227);
drawCircles(200,200,150,4);
fill(46,229,19);
drawCircles(200,200,150,5);
}

Tuesday 26 May 2009

Chunk 14: First 900 words....

{After lots of things getting in the way I'm pressing on quickly working from the outline I wrote back in January!}

Chunk 14: Functions 1

Up to this point all our coding has been linear: that is to say our sketches start at the top and work their way right through until they get to the bottom with everything spelt out in full.

There are times when we’ve almost repeated the same pieces of code multiple times within the same sketch. Wouldn’t it be great if there was a way that you could just write that bit once and then tell the computer to use it again and again? This is where ‘functions’ come in.

A function is a mini program that can be used over and over by a main program. Every function has a name and, optionally, has one or more arguments passed to it. The basic structure of a function is:

void function_name ( argument_list ) {
// function content
}

The ‘void’ indicates that the function does not return any value; the function name should follow the normal naming conventions that you would expect for any identifier; the round brackets enclose any arguments that the function will be expecting, these will be examined shortly; finally, the braces are used to mark the beginning and end of the code that comprises the function content, this can range from a simple statement of a single line to a complex routine of many lines.

The round brackets and braces must always be present even if they have nothing to enclose: that is to say, if there are no arguments, the round brackets are still present but with nothing between them; and if there is no code (if the function is a placeholder to be filled in later for example) then the braces are still present. In the latter case it is usual to insert a comment as a reminder about the empty function!

The argument list is where all the work is done: this is where all the differences are made for a function. Rather than always doing the same thing, using arguments allows us to influence the way that the function will operate. Arguments are lists in a similar fashion to the way that variables are declared except that they are separated by commas: so you would declare a variable type followed by a variable name, and then a comma followed by the same sequence repeated for however many arguments are required. If you ever find yourself writing a function with more than five or six arguments though, then your function is most likely trying to do too much and should be examined to see if it can be broken down into two or more simpler functions because the more complicated a function is, the harder it is to debug and the easier it is for an error to split through unnoticed.

Let us look at an example: we can specialise the standard ‘ellipse’ routine to make our own function to draw a circle, which we shall call ‘drawCircle’, to save having to repeat the third argument every time we want a circle and avoid the possibility of mistyping the repeat.

We already know what arguments we are going to need; we are mimicking the first two arguments of the ‘ellipse’ routine, but replacing the third and fourth with a single ‘diameter’. So our argument list is going to be ‘float x, float y, float diameter’.

These arguments we can pass straight on to the ellipse routine, passing the diameter for both the width and height arguments, without any additional processing, so our function will look like this:

void drawCircle(float x, float y, float diameter) {
ellipse(x,y,diameter,diameter);
}

A similar function named ‘drawSquare’ would specialise the rectangle routine as follows:

void drawSquare(float x, float y, float width) {
rectangle(x,y,width,width);
}

Now we have two functions, how do we use them? Well, they are used just like any normal routine, you give the function name and arguments in round brackets and the computer executes the function contents at that point in your code.

Let us write a short sketch to draw a circle on top of a square using the functions that we’ve just declared.

void setup(){
size(400,400);
background(255);
drawSquare(200,200,150);
drawCircle(200,200,150);
}

When we execute this we find that it’s not quite what we want.

The circle draws with the (x,y) arguments specifying the centre of the circle, whereas the square draws with the (x,y) arguments specifying the top left corner.

It might be useful to have a further function, say ‘drawSquareCentred’ that uses the (x,y) arguments to specify the centre in the same way as the circle.

Now, the only difference between the normal square and the ‘centred’ square is that an offset is going to be applied to the x and y arguments; so we’re going to use the fact that the functions become part of the system to expand on drawSquare and not repeat the rectangle command.

Our drawSquareCentred function is going to use a ‘local’ variable. A local variable is one that is declared like any other variable, but within the function braces, it cannot be accessed outside of the function itself, it is completely self contained: it is said to have a scope extending to the end of the function.

void drawSquareCentred(float x, float y, float width) {
float halfWidth = width/2 ;
drawSquare(x-halfWidth,y-halfWidth,width);
}

If we now modify our short test sketch to replace ‘drawSquare’ with drawSquareCentred’ we get the result we originally intended:

Monday 9 February 2009

And time goes on...

Chunk 14. Functions 1.

Yip, that's what I'm doing. I am doing it. Really. Yes.

I'm also learning Welsh (L196 Croeso: beginners Welsh), which may seem incompatible with a computing degree, but apparently 30 points can be used for anything, and I'm taking them at their word on that.

My main delay has been.... well actually I was writing an eBook on beginning programming in DAZ Studio Scripting. DAZ Studio is a 3D rendering package and the scripting is generally based on ECMA Script... I really didn't want to get Processing and DAZ Studio mixed up, so I got that one finished first since I'd started it first.

Now I can knuckle down to the jolly subject of functions and what they're all about. ;)

...and I'll try to resist using smilies.

Tuesday 13 January 2009

Plunge taken

I've taken the plunge at last and requested allocation of chunk 14: Functions 1.

It will make a change from writing about DAZ Studio scripting, that is all I seem to have been doing of late.

I'll wait for confirmation and then get stuck in.

Wednesday 19 November 2008

Starting slowly

Noswaith dda!

I've been somewhat distracted for the start of our endeavour: having started on L196 (Croeso: beginners' Welsh) at the start of the month, it's proving to be a completely different way of studying from my normal M*** courses. It's an interesting course, but one that I must remember is only supposed to take a little time a day.

Thoroughly enjoying dipping into Processing. Must select a chunk soon.

Hwyl.

Tuesday 28 October 2008

Aha! Book arrived.

Poor postman: he arrived this morning with two parcels from Amazon for me...

The first was a comparatively light one containing a USB soundcard that I've picked up so I can use Lyceum (for L196 Croeso: beginning Welsh) without having to sit under the desk!

The second, however, is much more interesting --- and decidedly heavier --- Processing by everyones favourite author Ira Greenberg. Oh yes!

I hadn't realised it was a hardback, nice presentation and good print quality.... oh, I'd better get reading before everyone picks the best chunks!