Tutorial 7: Drawing with Functions

Using Functions

You've already been using functions in your sketches, every time that you've drawn a line or set the colour to fill shapes you've called functions with values that specify what they should do, called "parameters".

Functions make code more concise by extracting the common elements of repeated code and making them into code blocks that can be run many times within a program. This can also make code much easier for read and update and reduces the chance of errors being introduced when code is repeated.

Parameters

Functions can take no parameters, or they can take multiple parameters that modify their actions. Parameters are defined for functions inside round brackets after the function's name.

Using a custom function

First, we'll look at how we might use a custom function. The following sketch contains a custom function called face() that we first saw in the lecture on Functions and Recursion.

  • Show Sketch
/** @peep sketchcode */
 
size(100, 100); 
smooth(); 
fill(0); 
face(20, 80, 26); 
 
void face(int x, int y, int gap) { 
  line(x, 0, x, y);               // Nose Bridge 
  line(x, y, x+gap, y);           // Nose 
  line(x+gap, y, x+gap, height); 
  int mouthY = (height+y)/2;
  line(x, mouthY, x+gap, mouthY); // Mouth 
  ellipse(x-gap/2, y/2, 5, 5);    // Left eye 
  ellipse(x+gap, y/2, 5, 5);      // Right eye 
}

NOTE: In the desktop version of Processing, when we want to use custom functions in a Processing sketch, we must first move our existing code inside a function called setup(), or split it across two functions, setup() and draw() if our sketch is to be animated. We will look at these functions in more detail later. If you want to run the above code in the desktop version of Processing you'll need to put the code outside of the face function in setup().

The face() function takes three parameters:

  1. An integer called x that defines the horizontal location of the top part, i.e., the bridge, of the nose.
  2. An integer called y that defines the vertical location of the end of the nose. The vertical positions of the nose and mouth are calculated relative to the end of the nose.
  3. An integer called gap that defines the width of the nose, as well as the separation of the eyes and the width of the mouth.

When a function is defined, the type of each parameter, e.g., int is provided before the name of the parameter that will be used to refer to the value provided within the body of the function. Note that this name does not have to match the name of any variables used to pass the function values.

Notice that the parameters are defined in round brackets after the name of the function in the same order that the values must be provided to the function. In the above sketch the face() function is called just once to draw the face with values for x, y and gap of 20, 80 and 26 respectively.

Edit the above sketch to increase the size of the canvas to 200x200 pixels and modify the values for x, y and gap so that they are calculated relative to the values for width and height provided by Processing such that the face is drawn in a similar location and size relative to the display.

Proportional Face

The power of defining functions that take parameters is that they can be called with different values without having to re-write the code.

Edit the sketch you created above to generate a different variation of the face each time the sketch is run. You should only have to edit one line of code.

A Custom Drawing Function

In this chapter we'll look at how we can create a function to draw a shape, similar to the functions provided by Peep. The shape we're going to draw is a star. To understand how we're going to draw a star in a function let's first write a sketch to explore the construction.

A regular star shape can be defined using a center point, an inner and outer radius and the number points that the star should have. Let's have a look at how we would draw this without using a function. First, we'll just use the regular drawing functions to draw some "construction lines" so that we can see where the points should be:

  • Show Code
/** @peep sketch */
 
size(200, 200);
 
// Define some variables to use to draw a star
float x = 100;
float y = 100;
float inner = 40;
float outer = 80;
int points = 5;
 
background(255);
 
// Draw the center point of the star
stroke(0, 0, 255, 127);
fill(0, 0, 255);
ellipse(x, y, 3, 3);
 
// Draw ellipses for the inner and outer radii of the points
noFill();
ellipse(x, y, 2*outer, 2*outer);
ellipse(x, y, 2*inner, 2*inner);
 
// Calculate the angle between points
float delta = radians(360/points);
 
// Draw lines where each of the outer points should be...
stroke(0, 255, 0, 127);
fill(0, 255, 0);
for (int i = 0; i < points; i++) {
  // Draw the outer point
  float ox = x + outer * cos(i*delta);
  float oy = y + outer * sin(i*delta);
  line(x, y, ox, oy);
  ellipse(ox, oy, 3, 3);
}
 
// Draw lines where each of the inner points should be...
stroke(255, 0, 0, 127);
fill(255, 0, 0);
for (int i = 0; i < points; i++) {
  // Draw the inner point
  float ix = x + inner * cos(i*delta + delta/2);
  float iy = y + inner * sin(i*delta + delta/2);
  line(x, y, ix, iy);
  ellipse(ix, iy, 3, 3);
}

In this sketch the inner and outer circles are draw in blue, the radii to the outer points are drawn in green and the radii to the inner points are drawn in red. Creating a sketch like this, using the built-in functions to draw "construction lines" can help us to identify useful code that we can use in the drawing of complex shapes. For example, we can see when we look at the above code that we should be able to combine the loops for drawing the outer points and the inner points:

  • Show Code
/** @peep sketch */
 
size(200, 200);
 
// Define some variables to use to draw a star
float x = 100;
float y = 100;
float inner = 40;
float outer = 80;
int points = 5;
 
background(255);
 
// Draw the center point of the star
stroke(0, 0, 255, 127);
fill(0, 0, 255);
ellipse(x, y, 3, 3);
 
// Draw ellipses for the inner and outer radii of the points
noFill();
ellipse(x, y, 2*outer, 2*outer);
ellipse(x, y, 2*inner, 2*inner);
 
// Calculate the angle between points
float delta = radians(360/points);
 
// Draw lines where each of the points should be...
for (int i = 0; i < points; i++) {
  // Draw the outer point
  float ox = x + outer * cos(i*delta);
  float oy = y + outer * sin(i*delta);
  stroke(0, 255, 0, 127);
  fill(0, 255, 0);
  line(x, y, ox, oy);
  ellipse(ox, oy, 3, 3);
  // Draw the inner point
  float ix = x + inner * cos(i*delta + delta/2);
  float iy = y + inner * sin(i*delta + delta/2);
  stroke(255, 0, 0, 127);
  fill(255, 0, 0);
  line(x, y, ix, iy);
  ellipse(ix, iy, 3, 3);
}

Now we're ready to draw the star, we'll add the code to the end of our existing sketch to make sure that we're calculating the correct points:

  • Show Code
/** @peep sketch */
 
size(200, 200);
 
// Define some variables to use to draw a star
float x = 100;
float y = 100;
float inner = 40;
float outer = 80;
int points = 5;
 
background(255);
 
// Draw the center point of the star
stroke(0, 0, 255, 127);
fill(0, 0, 255);
ellipse(x, y, 3, 3);
 
// Draw ellipses for the inner and outer radii of the points
noFill();
ellipse(x, y, 2*outer, 2*outer);
ellipse(x, y, 2*inner, 2*inner);
 
// Draw lines where each of the points should be...
float delta = radians(360/points);
for (int i = 0; i < points; i++) {
  // Draw the outer point
  float ox = x + outer * cos(i*delta);
  float oy = y + outer * sin(i*delta);
  stroke(0, 255, 0, 127);
  fill(0, 255, 0);
  line(x, y, ox, oy);
  ellipse(ox, oy, 3, 3);
  // Draw the inner point
  float ix = x + inner * cos(i*delta + delta/2);
  float iy = y + inner * sin(i*delta + delta/2);
  stroke(255, 0, 0, 127);
  fill(255, 0, 0);
  line(x, y, ix, iy);
  ellipse(ix, iy, 3, 3);
}
 
// Draw the star shape...
noFill();
stroke(0, 127);
beginShape();
delta = radians(360/points);
for (int i = 0; i < points; i++) {
  // Draw the outer point
  float ox = x + outer * cos(i*delta);
  float oy = y + outer * sin(i*delta);
  vertex(ox, oy);
  // Draw the inner point
  float ix = x + inner * cos(i*delta + delta/2);
  float iy = y + inner * sin(i*delta + delta/2);
  vertex(ix, iy);
}
endShape(CLOSE);

Notice how this sketch differs from the previous one with that last section of code that uses the beginShape() and endShape() functions to define our custom shape and uses the same calculations that we used to draw the construction lines before to determine where the vertices should be. That last piece of code that we added is the bit that we want to make a function out of. Here's how the function might look to start with:

    void star() {
      // Draw the star shape... 
      beginShape();
      float delta = radians(360/points);
      for (int i = 0; i < points; i++) {
        // Draw the outer point
        float ox = x + outer * cos(i*delta);
        float oy = y + outer * sin(i*delta);
        vertex(ox, oy);
        // Draw the inner point
        float ix = x + inner * cos(i*delta + delta/2);
        float iy = y + inner * sin(i*delta + delta/2);
        vertex(ix, iy);
      }
      endShape(CLOSE);
    }

    Notice that we didn't include the code that sets the fill or stroke colours. Like the built-in drawing functions, we want our custom function to use the current fill and stroke values, whatever they might be. And here's how we would use it:

    • Show Code
    /** @peep sketch */
     
    // Define some variables to use to draw a star
    float x = 100;
    float y = 100;
    float inner = 40;
    float outer = 80;
    int points = 5;
     
    size(200, 200);
    background(204);
    fill(255);
    stroke(0);
    strokeWeight(3);
    star();
     
    void star() {
      // Draw the star shape... 
      beginShape();
      float delta = radians(360/points);
      for (int i = 0; i < points; i++) {
        // Draw the outer point
        float ox = x + outer * cos(i*delta);
        float oy = y + outer * sin(i*delta);
        vertex(ox, oy);
        // Draw the inner point
        float ix = x + inner * cos(i*delta + delta/2);
        float iy = y + inner * sin(i*delta + delta/2);
        vertex(ix, iy);
      }
      endShape(CLOSE);
    }

    Adding Parameters

    In the version of our star() function that we've developed, we've just moved the code that we had previously inside a function. The star() function takes no parameters. i.e., the parentheses after the function name are empty, and returns no value, i.e., has a return type of void.

    This first version of the star() function relies on using the global variables x, y, inner, outer and points to draw the shape. In general, it is considered bad programming practice to write functions that rely on global variables, so we should first move those inside the function so that it is self-contained:

      void star() {
        // Define some variables to use to draw a star
        float x = 100;
        float y = 100;
        float inner = 40;
        float outer = 80;
        int points = 5;
        // Draw the star shape... 
        beginShape();
        float delta = radians(360/points);
        for (int i = 0; i < points; i++) {
          // Draw the outer point
          float ox = x + outer * cos(i*delta);
          float oy = y + outer * sin(i*delta);
          vertex(ox, oy);
          // Draw the inner point
          float ix = x + inner * cos(i*delta + delta/2);
          float iy = y + inner * sin(i*delta + delta/2);
          vertex(ix, iy);
        }
        endShape(CLOSE);
      }

      Now that our function is self-contained, we can start to turning some of our variables into parameters that can be passed to the function. To do this we, simply remove the definition of the variable from inside the function and add it to the list of parameters inside the round brackets. So to make x and y parameters of the function we would do this:

        void star(float x, float y) {
          // Define some variables to use to draw a star
          float inner = 40;
          float outer = 80;
          int points = 5;
          // Draw the star shape... 
          beginShape();
          float delta = radians(360/points);
          for (int i = 0; i < points; i++) {
            // Draw the outer point
            float ox = x + outer * cos(i*delta);
            float oy = y + outer * sin(i*delta);
            vertex(ox, oy);
            // Draw the inner point
            float ix = x + inner * cos(i*delta + delta/2);
            float iy = y + inner * sin(i*delta + delta/2);
            vertex(ix, iy);
          }
          endShape(CLOSE);
        }

        Now when we call the function we will have to remember to supply the values for the parameters, e.g.:

          star(100, 100); // Draw a regular 5-pointed star at (100, 100)

          We can do the same with the inner and outer variables, making them parameters that must be supplied when calling the star() function:

            void star(float x, float y, float inner, float outer) {
              // Define some variables to use to draw a star
              int points = 5;
              // Draw the star shape... 
              beginShape();
              float delta = radians(360/points);
              for (int i = 0; i < points; i++) {
                // Draw the outer point
                float ox = x + outer * cos(i*delta);
                float oy = y + outer * sin(i*delta);
                vertex(ox, oy);
                // Draw the inner point
                float ix = x + inner * cos(i*delta + delta/2);
                float iy = y + inner * sin(i*delta + delta/2);
                vertex(ix, iy);
              }
              endShape(CLOSE);
            }

            We now have a function that we can call multiple times with different values for the parameters x, y, inner and outer to draw regular 5-pointed stars at different locations and of different sizes and shapes:

            • Show Sketch
            /** @peep sketchcode */
             
            size(200, 200);
            background(204);
            strokeWeight(4);
            star(100, 100, 20, 40);
            star(30, 80, 20, 30);
            star(150, 50, 15, 40);
             
            void star(float x, float y, float inner, float outer) {
              // Define some variables to use to draw a star
              int points = 5;
              // Draw the star shape... 
              beginShape();
              float delta = radians(360/points);
              for (int i = 0; i < points; i++) {
                // Draw the outer point
                float ox = x + outer * cos(i*delta);
                float oy = y + outer * sin(i*delta);
                vertex(ox, oy);
                // Draw the inner point
                float ix = x + inner * cos(i*delta + delta/2);
                float iy = y + inner * sin(i*delta + delta/2);
                vertex(ix, iy);
              }
              endShape(CLOSE);
            }

            Practice drawing with loops by drawing a grid of stars with our new custom function.

            Grid of Five-Pointed Stars

            Modify the star() function above to accept an additional parameter to control the number of points for the star.

            Stars with Variable Numbers of Points

            We can use the new function that we're developed to generate variations using the random() function. allowing us to quickly explore the space of possibilities offered by the function.

            Write a sketch to generate the following drawing using your extended star() function.

            Grid of Stars

            Notice that one of the few things about the stars that we haven't added as a parameter is the direction of the first point, in all of the versions of this function the first point is always oriented to the right, along the horizontal axis. We could extend the star() function further to add an angle that would control this direction, but this would be out of character with the built-in functions, i.e., rect() doesn't directly support the drawing of rotated rectangles, so we're going to leave this function the way it is. As we'll see in a future tutorial, Processing supports the general transformation of drawing functions, including the rotation of any shape, so this is something that can be done outside of the star() function, just like the setting of stroke and fill colours. This type of decision, i.e., to follow the convention of the built-in drawing functions, is the sort of thing that you might choose to put in comments about a function so that you can understand why the function is coded the way it is at a later stage and how you propose that the function should be used.

            Functions Calling Functions

            In the previous chapter we developed a custom function and called that from the our main program. There is nothing stopping us from writing functions that call other functions - in fact, we did this when we called any drawing functions above.

            A common reason that we might want to write our own functions that call other functions is to "wrap" an existing function with a simpler interface. For example, we might write functions for drawing circles or squares that provide simpler interfaces for drawing these shapes compared to the ellipse() and rect() function that they rely on:

              void circle(float x, float y, float diameter) {
                ellipse(x, y, diameter, diameter);
              }
               
              void square(float x, y, float side) {
                rect(x, y, side, side);
              }

              The advantage of writing these types of functions is that the simpler interface reduces the likelihood of making an error, it also helps the code to be a little more self-documenting, i.e., it is more obvious when using one of these functions that the drawing will contain circles or squares than when using the functions for drawing ellipses or rectangles.

              Another reason for writing functions that call other functions is to build on the functionality of other functions to achieve more complex tasks. To explore this we'll experiment with the face() function that we used earlier. Here's the function as we used it previously:

                void face(int x, int y, int gap) { 
                  line(x, 0, x, y);               // Nose Bridge 
                  line(x, y, x+gap, y);           // Nose 
                  line(x+gap, y, x+gap, height); 
                  int mouthY = (height+y)/2;
                  line(x, mouthY, x+gap, mouthY); // Mouth 
                  ellipse(x-gap/2, y/2, 5, 5);    // Left eye 
                  ellipse(x+gap, y/2, 5, 5);      // Right eye 
                }

                We want to write a new function that will allow us to draw a "crowd" of these faces. The first thing we'll want to do if put some bounds around the face, so that the drawing no longer covers the whole display window. We'll do this by adding parameters to control the position and size of the face. We'll start by renaming the current x and y parameters to something so that we can use those for the position of the face, while we're at it we'll also rename the gap parameter to make the code a little easier to read:

                  void face(int noseX, int noseY, int noseWidth) { 
                    line(noseX, 0, noseX, noseY);               // Nose Bridge 
                    line(noseX, noseY, noseX+noseWidth, noseY);           // Nose 
                    line(noseX+noseWidth, noseY, noseX+noseWidth, height); 
                    int mouthY = (height+noseY)/2;
                    int mouseWidth = noseWidth;
                    line(noseX, mouthY, noseX+noseWidth, mouthY); // Mouth 
                    int eyeY = noseY/2;
                    int eyeSeparation = noseWidth/2;
                    ellipse(noseX-eyeSeparation, eyeY, 5, 5);    // Left eye 
                    ellipse(noseX+eyeSeparation, eyeY, 5, 5);      // Right eye 
                  }

                  Now we can add some some additional parameters to control the location and size of the face:

                    void face(int faceX, int faceY, int faceWidth, int faceHeight, int noseX, int noseY, int noseWidth) { 
                      fill(255);
                      rect(faceX, faceY, faceWidth, faceHeight);
                      line(faceX+noseX, faceY, faceX+noseX, faceY+noseY); // Nose Bridge 
                      line(faceX+noseX, faceY+noseY, faceX+noseX+noseWidth, faceY+noseY); // Nose 
                      line(faceX+noseX+noseWidth, faceY+noseY, faceX+noseX+noseWidth, faceY+faceHeight); 
                      int mouthY = (faceHeight+noseY)/2;
                      int mouseWidth = noseWidth;
                      line(faceX+noseX, faceY+mouthY, faceX+noseX+noseWidth, faceY+mouthY); // Mouth 
                      int eyeY = noseY/2;
                      int eyeSeparation = noseWidth/2;
                      fill(0);
                      ellipse(faceX+noseX-eyeSeparation, faceY+eyeY, 5, 5); // Left eye 
                      ellipse(faceX+noseX+eyeSeparation, faceY+eyeY, 5, 5); // Right eye 
                    }

                    We can now produce a sketch that draws multiple faces in the same display window:

                    • Show Code
                    /** @peep sketch */
                    void setup() {
                      size(200, 200);
                      background(204);
                      face(20, 20, 40, 60, 15, 20, 10);
                      face(100, 80, 60, 60, 30, 20, 15);
                    }
                     
                    void face(int faceX, int faceY, int faceWidth, int faceHeight, int noseX, int noseY, int noseWidth) { 
                      fill(255);
                      rect(faceX, faceY, faceWidth, faceHeight);
                      line(faceX+noseX, faceY, faceX+noseX, faceY+noseY); // Nose Bridge 
                      line(faceX+noseX, faceY+noseY, faceX+noseX+noseWidth, faceY+noseY); // Nose 
                      line(faceX+noseX+noseWidth, faceY+noseY, faceX+noseX+noseWidth, faceY+faceHeight); 
                      int mouthY = (faceHeight+noseY)/2;
                      int mouseWidth = noseWidth;
                      line(faceX+noseX, faceY+mouthY, faceX+noseX+noseWidth, faceY+mouthY); // Mouth 
                      int eyeY = noseY/2;
                      int eyeSeparation = noseWidth/2;
                      fill(0);
                      ellipse(faceX+noseX-eyeSeparation, faceY+eyeY, 5, 5); // Left eye 
                      ellipse(faceX+noseX+eyeSeparation, faceY+eyeY, 5, 5); // Right eye 
                    }

                    The next step is to write our function for drawing a crowd of faces. The function will use five parameters to define a rectangular area of the display to draw the crowd within, and the number of faces to draw the crowd with:

                      void crowd(int crowdX, int crowdY, int crowdWidth, int crowdHeight, int numFaces) {
                        for (int f = 0; f < numFaces; f++) {
                          int faceWidth = int(random(crowdWidth/12, crowdWidth/6));
                          int faceHeight = int(random(crowdHeight/12, crowdHeight/6));
                          int faceX = int(random(crowdX, crowdX+crowdWidth)) - faceWidth/2;
                          int faceY = int(random(crowdY, crowdY+crowdHeight)) - faceHeight/2;
                          int noseX = int(random(faceWidth/8, 7*faceWidth/8));
                          int noseY = int(random(faceHeight/4, 3*faceHeight/4));
                          int noseWidth = int(random(faceWidth/6, faceWidth/3));
                          face(faceX, faceY, faceWidth, faceHeight, noseX, noseY, noseWidth);
                        }
                      }

                      The function adapts all of the ranges used to determine the parameters for drawing faces based on the parameters passed to it. Here's a sketch that uses the function:

                      • Show Code
                      /** @peep sketch */
                      void setup() {
                        size(200, 200);
                        background(204);
                        crowd(20, 20, width-40, height-40, 40);
                      }
                       
                      void crowd(int crowdX, int crowdY, int crowdWidth, int crowdHeight, int numFaces) {
                        for (int f = 0; f < numFaces; f++) {
                          int faceWidth = int(random(crowdWidth/12, crowdWidth/6));
                          int faceHeight = int(random(crowdHeight/12, crowdHeight/6));
                          int faceX = int(random(crowdX, crowdX+crowdWidth)) - faceWidth/2;
                          int faceY = int(random(crowdY, crowdY+crowdHeight)) - faceHeight/2;
                          int noseX = int(random(faceWidth/8, 7*faceWidth/8));
                          int noseY = int(random(faceHeight/4, 3*faceHeight/4));
                          int noseWidth = int(random(faceWidth/6, faceWidth/3));
                          face(faceX, faceY, faceWidth, faceHeight, noseX, noseY, noseWidth);
                        }
                      }
                       
                       
                      void face(int faceX, int faceY, int faceWidth, int faceHeight, int noseX, int noseY, int noseWidth) { 
                        fill(255);
                        rect(faceX, faceY, faceWidth, faceHeight);
                        line(faceX+noseX, faceY, faceX+noseX, faceY+noseY); // Nose Bridge 
                        line(faceX+noseX, faceY+noseY, faceX+noseX+noseWidth, faceY+noseY); // Nose 
                        line(faceX+noseX+noseWidth, faceY+noseY, faceX+noseX+noseWidth, faceY+faceHeight); 
                        int mouthY = (faceHeight+noseY)/2;
                        int mouseWidth = noseWidth;
                        line(faceX+noseX, faceY+mouthY, faceX+noseX+noseWidth, faceY+mouthY); // Mouth 
                        int eyeY = noseY/2;
                        int eyeSeparation = noseWidth/2;
                        fill(0);
                        ellipse(faceX+noseX-eyeSeparation, faceY+eyeY, faceWidth/20, faceWidth/20); // Left eye 
                        ellipse(faceX+noseX+eyeSeparation, faceY+eyeY, faceWidth/20, faceWidth/20); // Right eye 
                      }

                      Extend the crowd() and face() functions to draw faces of different colours.

                      Functions Calling Themselves

                      As well as calling other functions, we can write functions that call themselves. This is called recursion and functions that call themselves are called recursive functions.

                      Recursion is a common technique for drawing complex self-similar, e.g., fractal-like, shapes. Here's an example of a recursive function:

                      • Show Code
                      /** @peep sketch */
                      size(200, 200);
                      squareRecursion(0, 0, width, height);
                       
                      void squareRecursion(float x, float y, float w, float h) {
                        // Draw a rectangle
                        noFill();
                        stroke(0);
                        rect(x, y, w, h);
                       
                        // Test to see if we should recurse...
                        if (w > 10 || h > 10) {
                          // Set a border to go around sub-rectangles
                          float bx = 3;
                          float by = 3;
                          // Calculatue the width and height of sub-rectangles
                          w = (w - 3*bx) / 2;
                          h = (h - 3*by) / 2;
                          // Call the same function to draw the sub-rectangles
                          squareRecursion(x + bx, y + by, w, h);
                          squareRecursion(x + bx + w + bx, y + by, w, h);
                          squareRecursion(x + bx, y + by + h + by, w, h);
                          squareRecursion(x + bx + w + bx, y + by + h + by, w, h);
                        }
                      }

                      It is important when writing recursive functions that there is some way for the program to know when to stop. In the above example this is done with the test if (w > 10 || h > 10) { ..., this makes sure that when the square becomes too small to subdivide the functions stops calling itself. We can use random() in our tests to recurse different amounts, which can create interesting effects:

                      • Show Code
                      /** @peep sketch */
                      size(200, 200);
                      squareRecursion(0, 0, width, height);
                       
                      void squareRecursion(float x, float y, float w, float h) {
                        // Draw a rectangle
                        noFill();
                        stroke(0);
                        rect(x, y, w, h);
                       
                        // Test to see if we should recurse...
                        if ((random(100) < 80) && (w > 10 || h > 10)) {
                          // Set a border to go around sub-rectangles
                          float bx = 3;
                          float by = 3;
                          // Calculatue the width and height of sub-rectangles
                          w = (w - 3*bx) / 2;
                          h = (h - 3*by) / 2;
                          // Call the same function to draw the sub-rectangles
                          squareRecursion(x + bx, y + by, w, h);
                          squareRecursion(x + bx + w + bx, y + by, w, h);
                          squareRecursion(x + bx, y + by + h + by, w, h);
                          squareRecursion(x + bx + w + bx, y + by + h + by, w, h);
                        }
                      }

                      Modify the above code to change the way that the squares draw themselves before recursing into the next level. For example, can you draw the squares with different transparent fill colours:

                      Colourful Recursive Squares 1Colourful Recursive Squares 2Colourful Recursive Squares 3Colourful Recursive Squares 4

                      Comments

                        Andrew Holdaway about a year ago

                        /** @peep sketch */

                        size(200, 200);

                        // Define some variables to use to draw a star float x = 100; float y = 100; float inner = 40; float outer = 80; int points = 5;

                        background(255);

                        // Draw the center point of the star stroke(0, 0, 255, 127); fill(0, 0, 255); ellipse(x, y, 3, 3);

                        // Draw ellipses for the inner and outer radii of the points noFill(); ellipse(x, y, 2outer, 2outer); ellipse(x, y, 2inner, 2inner);

                        // Draw lines where each of the points should be... float delta = radians(360/points); for (int i = 0; i < points; i++) { // Draw the outer point float ox = x + outer * cos(idelta); float oy = y + outer * sin(idelta); stroke(0, 255, 0, 127); fill(0, 255, 0); line(x, y, ox, oy); ellipse(ox, oy, 3, 3); // Draw the inner point float ix = x + inner * cos(idelta + delta/2); float iy = y + inner * sin(idelta + delta/2); stroke(255, 0, 0, 127); fill(255, 0, 0); line(x, y, ix, iy); ellipse(ix, iy, 3, 3); } size(200, 200); background(204); fill(255); stroke(0); strokeWeight(1);

                        for(i=0;i<height;i+=20){ for(x=0;x<width;x+=20){ star(x+10,i+10,5,10,points); } }

                        endShape(CLOSE); void star(float x, float y, float inner, float outer) { // Define some variables to use to draw a star int points = 5; // Draw the star shape... beginShape(); float delta = radians(360/points); for (int i = 0; i < points; i++) { // Draw the outer point float ox = x + outer * cos(idelta); float oy = y + outer * sin(idelta); vertex(ox, oy); // Draw the inner point float ix = x + inner * cos(idelta + delta/2); float iy = y + inner * sin(idelta + delta/2); vertex(ix, iy); } endShape(CLOSE); }