12 Koch Curve

advertisement
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.
Download