Recursion and Fractals: the Koch Curve In programming, “recursion” is when a method calls itself. Why would you need it? Sometimes you need the result of the previous calculation in order to be able to calculate the current one. Or, you need the result of a previous task to process the current task. A simple mathematical example is when you want to multiply all the numbers from 1 to a number ‘n’. This is called ‘n-factorial’ and is written ‘n!’ (if you have a scientific calculator, you can try it out). n! can be defined as n x (n-1)! For example, 5! = 5 x 4! Because 5! = 5x4x3x2x1 and 4! = 4x3x2x1 You can write a short method to calculate factorials: public static long factorial(long n) { if (n == 0) return 1; else return n * factorial(n-1); } There are two key points: 1. In the “else” part, the method calls itself with the value of n-1 2. It has to have some way of stopping, so that it doesn’t keep calling itself forever. In this case, it stops when n=0 by simply returning the value 1 (because 0! Is defined as 1). Assignment: The Koch Curve Fractals are a kind of geometric pattern or picture that uses recursion. One of the simplest fractals is called the Koch curve. To create the Koch curve, you start with a single straight line. Replace the straight line with a pattern like: Now keep repeating this process, replacing each straight line with the pattern. The first step looks like: The second step looks like: We can generate this kind of pattern by writing a recursive method. The method must be given the starting and ending points of a line. It will then calculate the coordinates of the starting and ending points of the 4 new line segments in the pattern. After drawing these line segments, the method will use itself to replace each of the segments with the pattern. The only problem is that we must prevent it from going on and on forever. We can do this by specifying the number of iterations we want, ‘n’, and passing this to the method. Each time the method calls itself it passes ‘n-1’. When it receives a value of zero, it knows to stop. Here is a method that will generate the Koch curve. The calculation of the coordinates of the start and end points is difficult (it involves some trigonometry). public void koch (Graphics g, int x1, int y1, int x5, int y5, int n) { int x2, y2, x3, y3, x4, y4; double d, a, h; g.setColor (Color.black); g.drawLine (x1, getHeight () - y1, x5, getHeight () - y5); //Draw the line if (n == 0) return; d = Math.sqrt ((x5-x1) * (x5-x1) + (y5-y1) * (y5-y1)) / 3; a = Math.atan2 ((double) (y5-y1), (double) (x5-x1)); h = 2 * d * Math.cos (30 * Math.PI / 180); x2 y2 x3 y3 x4 y4 = = = = = = x1 y1 x1 y1 x1 y1 + + + + + + (int) (int) (int) (int) (int) (int) (d (d (h (h (2 (2 * * * * * * Math.cos (a)); Math.sin (a)); Math.cos ((a + 30 * Math.PI / 180))); Math.sin ((a + 30 * Math.PI / 180))); d * Math.cos (a)); d * Math.sin (a)); g.setColor (Color.white); g.drawLine (x2, getHeight () - y2, x4, getHeight () - y4); koch koch koch koch (g, (g, (g, (g, x1, x2, x3, x4, y1, y2, y3, y4, x2, x3, x4, x5, y2, y3, y4, y5, n n n n - 1); 1); 1); 1); // 1/3 of the length // angle of the line // distance to peak // coordinates of peak // Erase the middle // Recursive calls to replace line with new pattern } Create a new Applet project. Add this method. Add a line to the paint() method to draw a Koch curve: koch (g, 50, 100, 350, 100, 2); Try changing the last value (‘n’). You will get an error if ‘n’ is too large because the line segments get so small that x5-x1 becomes zero, causing a division by zero error. You can avoid this by exiting when x5-x1 is zero. Change if (n == 0) to if (n == 0 || (x5 - x1) == 0) Notice that the middle section does not get erased perfectly. This results from rounding the numbers to integers. We can avoid this by not drawing the original line and then erasing it, but just drawing the final line segments. Delete the two g.setColor() / g.drawLine() sections and replace the exit condition (if (n == 0 …) with: if (n == 0 || (x5 - x1) == 0) { g.drawLine (x1, getHeight() - y1, x5, getHeight() - y5); //Draw the line return; } This exits the method as before but also draws the final, smallest line segments. To make the program user-friendly, you could add a text field that would allow the user to enter the value of ‘n’. To the init() method add: For Applet: text = new TextField (10); this.add (text); For JApplet: text = new TextField (10); JPanel panel = new JPanel(); panel.add (text); this.add (panel); JApplets need a panel because otherwise the text field will fill the entire screen and cover the Koch Curve. In the paint() method, get the value of “text” – text.getText(), convert it to an integer (e.g. “n”) using Integer.parseInt() and pass it to the koch() method. You should put it in a try / catch block to prevent NumberFormatException errors. Add an action() method : public boolean action (Event evt, Object obj) { repaint (); return true; } If you are using a JApplet, you will want to clear the screen. Add the following as the FIRST LINE of the paint method: (this calls JApplet’s paint method) super.paint(g); To make your Applet scaleable, replace the line’s endpoint with getBounds().width.