Here are the DrawFunction
subclasses used to create and draw the fractal patterns for the
FractalsApplet
demonstration program. The algorithms
were described earlier.
The key features of the draw()
method in DrawBifurcation
are the three loops. The outer loop cycles through the columns (i.e.
the x horizontal
coordinate) of the plot. The first inner loop sets all the points
on each column to a fixed background color. The second inner loop
implements the heart of the algorithm. The given formula is interated
for MAX_ITERATIONS
number of times. After skipping an initial set of interations so
as to reach the orbit behavior of the iterated values, a color is
draw for each row (i.e. point on the y
axis) obtained from a scaling of the iterated variable z.
The draw()
methods for the other three algorithms also eacj include three key
loops but the structure differs. The outer loop cycles over the
rows (i.e. x
values) and a second nested loop cycles through the columns (i.e.
y
values). A third loop iterates the function starting from a value
determined by the current column, row point (i.e. x_beg,
y_beg).
After the loop either finishes or "escapes", the final
iterated value determines the color at the column, row (i.e. x,y)
point.
In all four cases, the drawLine()
method in Graphics
is used to draw each point of the pattern. This involves many invocatins
of drawLine()
and an alternative method involving the modification of pixel values
in an image is used instead in the next demonstration
program to speed up the display.
Note that for the complex numbers,
we use the Complex.java
class from Chapter 4: Tech.
Unlike the immutable complex class discussed in Chapter
5: Tech, this class allows direct access to the real and imaginary
data variables. Since the processing here is quite intensive, we
decided to take advantage of this capability to obtain faster performance.
The immutable class requires method invocations to access the real
and imaginary data and also require that new objects be created
for every new complex value. These all take up some amount of time,
which may or may not be noticable depending on the processor.
Fractal
Algorithm Classes used in FractalsApplet
DrawBifurcation.java
- DrawFunction subclass that draws fractal pattern generated
with the Bifurcation algorithm.
DrawJuliaSet.java
- DrawFunction subclass that draws fractal pattern generated
with the Julia Set algorithm.
DrawJuliaSetNewton.java
- DrawFunction subclass that draws fractal generated pattern
with the Julia Set via Newton's method algorithm.
DrawMandlebrot.java
- DrawFunction subclass that draws fractal pattern generated
with the Mandlebrot algorithm.
+
Previous class:
Chapter
4:Tech: Complex.java
|
import
java.awt.*;
/**
* Draw patterns with the bifurcation algorithm
on a graphics
* context passed to it.
*
* It follows the algorithm as given in "Java
Number Cruncher"
* by R. Mak.
**/
public class DrawBifurcation extends DrawFunction
{
private static final int MAX_ITERATIONS
= 200;
private static final int SKIP_INTERATIONS = 50;
/**
* Execute the bifurcation
algorithm onto the PlotPanel.
*
* @param g graphics context
* @param frame_width
display area width in pixels.
* @param frame_height
display area height in pixels.
* @param frame_start_x
horizontal point on display where
* drawing
starts in pixel number.
* @param frame_start_y
vertical point on display where
* drawing
starts in pixel number.
* @param x_scale 2 dimensional
array holding lower and
* upper values
of the function input scale range.
* @param y_scale 2 dimensional
array holding lower and
* upper values
of the function output scale range.
**/
public void draw (Graphics g,
int frame_start_x, int frame_start_y,
int frame_width, int frame_height,
double [] x_scale, double [] y_scale) {
Color save_color = g.getColor ();
int y_max_index = y_scale.length
- 1;
// Don't draw outside of the frame.
g.setClip (frame_start_x+1, frame_start_y+1,
frame_width-1, frame_height-1);
// Check if ready to draw the line
if (fParameters == null) return;
// Get conversion factors from data
scale to frame pixels
double x_scale_factor = fParameters[0]/frame_width;
double y_scale_factor = fParameters[1]/frame_height;
// Coordinates in pixel scale
int row = 0;
int col = 0;
// Outer loop steps through the
c values along the horizontal axis.
for (col = 0; col < frame_width;
col++) {
// Change
from pixel scale to the data scale
double c
= x_scale[0] + col * x_scale_factor;
double z
= fParameters[2]; // Pass the starting value.
// Draw
the background color for each column.
g.setColor
(fColor);
for (row=0;
row < frame_height; row++) {
g.drawLine
(col+frame_start_x, row+frame_start_y,
col+frame_start_x,
row+frame_start_y);
}
// Now on
this column, plot z along the vertical axis as determined
// by the
function:
//
z = z*z + c
// Plot
it for MAX_ITERATIONS number of times. However, don't start
// plotting
until after SKIP_ITERATIONS number of times so as to
// reach
the orbit behavior of the function.
g.setColor
(Color.RED);
for (int
i=0; i < MAX_ITERATIONS; i++) {
z
= z*z + c;
if
(i >= SKIP_INTERATIONS) {
//
Convert from data scale to pixel scale
row
= (int)Math.round ((y_scale[y_max_index] - z)/y_scale_factor);
//
Use the drawLine method to plot a point.
g.drawLine
(col+frame_start_x, row+frame_start_y,
col+frame_start_x,
row+frame_start_y);
}
}
}
g.setColor (save_color);
} // draw
} // class DrawBifurcation
|
/**
* Draw patterns with the Julia set algorithm on
a graphics
* context passed to it.
*
* The class follows the algorithm as given in
"Java Number Cruncher"
* by R. Mak.
**/
public class DrawJuliaSet extends DrawFunction
{
private static final int MAX_ITERATIONS
= 32;
private static final int ESCAPE_MAGNITUDE = 2;
/**
* Draw fractal patterns
with the Julia set algorithm.
*
* @param g graphics context
* @param frame_width display
area width in pixels.
* @param frame_height display
area height in pixels.
* @param frame_start_x
horizontal point on display where
* drawing starts
in pixel number.
* @param frame_start_y
vertical point on display where
* drawing starts
in pixel number.
* @param x_scale 2 dimensional
array holding lower and
* upper values
of the function input scale range.
* @param y_scale 2 dimensional
array holding lower and
* upper values
of the function output scale range.
**/
public void draw (Graphics g,
int frame_start_x, int frame_start_y,
int frame_width, int frame_height,
double [] x_scale, double [] y_scale) {
Color save_color = g.getColor ();
int y_max_index = y_scale.length -
1;
// Don't draw outside of the frame.
g.setClip (frame_start_x+1, frame_start_y+1,
frame_width-1, frame_height-1);
// Check if ready to draw the line
if (fParameters == null) return;
// Get conversion factors from data
scale to frame pixels
double x_scale_factor = fParameters[0]/frame_width;
double y_scale_factor = fParameters[1]/frame_height;
// Obtain the complex constant from
the user interface.
Complex c = new Complex (fParameters[2],
fParameters[3]);
Complex z = new Complex (0.0,0.0);
// Coordinates in pixel scale
int row = 0;
int col = 0;
// Outer loop steps through the c
value along the horizontal axis.
for (row = 0; row < frame_height;
row++) {
// Change
from pixel scale to the data scale
double y_beg
= y_scale[y_max_index] - row * y_scale_factor;
for (col =
0; col < frame_width; col++) {
double
x_beg = x_scale[0] + col * x_scale_factor;
z.real
= x_beg; z.img = y_beg; // Set the starting value.
boolean
escaped = false;
int
iter = 0;
double
x = x_beg;
double
y = y_beg;
double
mag = 0.0;
//
Iterate the function:
//
z = z*z + c
do
{
z.multiply
(z);
z.add
(c);
mag
= z.modulus ();
escaped
= mag > ESCAPE_MAGNITUDE;
++iter;
}
while (iter < MAX_ITERATIONS && (!escaped));
//
If escaped, then set color to a shade of gray
//
proportional to the number of interations. The
//
more iterations, the darker
if
(escaped) {
int
gray = 0xFF - (0xFF*iter)/MAX_ITERATIONS;
gray
= Math.min (gray, 240);
g.setColor
(new Color (gray, gray, gray));
g.drawLine
(col+frame_start_x, row+frame_start_y,
col+frame_start_x, row+frame_start_y);
}
else {
//
Did not escape so relate the color instead to the
//
final magnitude of z.
int
i = ((int)(100*mag)) / ESCAPE_MAGNITUDE + 1;
int
red = (425*i) & 0xFF;
int
grn = (375*i) & 0xFF;
int
blu = (325*i) & 0xFF;
g.setColor
(new Color (red,grn,blu));
//
Use the drawLine method to plot a point.
g.drawLine
(col+frame_start_x, row+frame_start_y,
col+frame_start_x,
row+frame_start_y);
}
}
}
g.setColor (save_color);
} // draw
} // class DrawJuliaSet
|
import
java.awt.*;
/**
* Draw patterns with the Julia set via Newton's
method.
*
* Follow the algorithm given in "Java Number Cruncher"
* by R. Mak for solving f(z) = z^3 - 1 with Newton's
algorithm.
*
**/
public class DrawJuliaSetNewton extends DrawFunction
{
private static final int MAX_ITERATIONS
= 100;
// Create two complex constants needed in the
algorthm.
private static final Complex Z_ONE
= new Complex (1,0);
private static final Complex Z_THREE = new Complex
(3,0);
// Create a pool of complex numbers to use in
the algorithm
private Complex fZ0 = new Complex (0.0, 0.0);
private Complex fZ2 = new Complex (0.0, 0.0);
private Complex fZ3 = new Complex (0.0, 0.0);
/**
*
* Create fractal patterns
with the Julia set from Newton's
* method algorithm and
draw them with the graphics context.
*
* @param g graphics context
* @param frame_width display
area width in pixels.
* @param frame_height display
area height in pixels.
* @param frame_start_x
horizontal point on display where
* drawing starts
in pixel number.
* @param frame_start_y
vertical point on display where
* drawing starts
in pixel number.
* @param x_scale 2 dimensional
array holding lower and
* upper values
of the function input scale range.
* @param y_scale 2 dimensional
array holding lower and
* upper values
of the function output scale range.
**/
public void draw (Graphics g,
int frame_start_x, int frame_start_y,
int frame_width, int frame_height,
double [] x_scale, double [] y_scale) {
Color save_color = g.getColor ();
int y_max_index = y_scale.length -
1;
// Don't draw outside of the frame.
g.setClip (frame_start_x+1, frame_start_y+1,
frame_width-1, frame_height-1);
// Check if ready to draw the line
if (fParameters == null) return;
// Get conversion factors from data
scale to frame pixels
double x_scale_factor = fParameters[0]/frame_width;
double y_scale_factor = fParameters[1]/frame_height;
// Create our complex variable for
the algorithm.
Complex z = new Complex
(0.0,0.0);
// Coordinates in pixel scale
int row = 0;
int col = 0;
// Outer loop steps through the c
value along the horizontal axis.
for (row = 0; row < frame_height;
row++) {
// Change
from pixel scale to the data scale
double y_beg
= y_scale[y_max_index] - row * y_scale_factor;
for (col =
0; col < frame_width; col++) {
double
x_beg = x_scale[0] + col * x_scale_factor;
//
Create the z for this point on the complex plane
z.real
= x_beg; z.img = y_beg; // Set the starting value.
int
iter = 0;
//
Iterate the function:
//
z = z*z*z - 1
do
{
//
Use the pool complex objects to hold intermediate values
fZ0.real
= z.real; fZ0.img = z.img; // save z in fZ0
z.multiply
(z); // z = z * z
fZ2.real
= z.real; fZ2.img = z.img; // save z*z in fZ2
z.multiply
(fZ0);// z = z * z * z
fZ3.real
= z.real; fZ3.img = z.img; // save z*z*z in fZ3
//
Now comute the iteration formula from Newton's method.
//
(z^3 -1.0)/(3.0 * z^2)
fZ3.subtract
(Z_ONE); // fZ3 = z*z*z - 1
fZ2.multiply
(Z_THREE); // fZ2 = 3.0 * z*z
z.real
= fZ3.real; z.img = fZ3.img;
z.divide
(fZ2); // z = (z^3 - 1.0)/(3.0 * z^2)
++iter;
}
while (iter < MAX_ITERATIONS && (!z.equals (fZ0)) );
//
Set color proportional to the number of interations.
int
colorComp = 20 * (iter%10);
if
(z.real > 0.0) { // root = 1
g.setColor
(new Color (colorComp, colorComp, 0xFF));
g.drawLine
(col+frame_start_x, row+frame_start_y,
col+frame_start_x,
row+frame_start_y);
}
else
if
(z.img > 0.0) { // root ~= -0.5 + .9i
g.setColor
(new Color (colorComp, 0xFF, colorComp));
g.drawLine
(col+frame_start_x, row+frame_start_y,
col+frame_start_x,
row+frame_start_y);
}
else { // root ~= -0.5 - 0.9i
g.setColor
(new Color (0xFF, colorComp, colorComp));
g.drawLine
(col+frame_start_x, row+frame_start_y,
col+frame_start_x,
row+frame_start_y);
}
}
}
g.setColor (save_color);
} // draw
} // class DrawJuliaSetNewton
|
import
java.awt.*;
/**
* Draw patterns with the Mandlebrot algorithm.
*
* Follows the algorithm given in "Java Number
Cruncher"
* by R. Mak for solving f(z) = z^3 -1 with Newton's
* algorithm.
**/
public class DrawMandlebrot extends DrawFunction
{
private static final int MAX_ITERATIONS
= 32;
private static final int ESCAPE_MAGNITUDE = 2;
// Create two complex constants needed in the
algorthm.
private static final Complex Z_ONE
= new Complex (1,0);
private static final Complex Z_THREE = new Complex
(3,0);
// Create a pool of complex numbers to use in
the algorithm
private Complex fZ0 = new Complex (0.0, 0.0);
private Complex fZ2 = new Complex (0.0, 0.0);
private Complex fZ3 = new Complex (0.0, 0.0);
/**
* Create fractal patterns
with the Mandlebrot
* algorithm and draw them
with the graphics context.
*
* @param g graphics context
* @param frame_width display
area width in pixels.
* @param frame_height display
area height in pixels.
* @param frame_start_x
horizontal point on display where
* drawing starts
in pixel number.
* @param frame_start_y
vertical point on display where
* drawing starts
in pixel number.
* @param x_scale 2 dimensional
array holding lower and
* upper values
of the function input scale range.
* @param y_scale 2 dimensional
array holding lower and
* upper values
of the function output scale range.
**/
public void draw (Graphics g,
int frame_start_x, int frame_start_y,
int frame_width, int frame_height,
double [] x_scale, double [] y_scale) {
Color save_color = g.getColor ();
int y_max_index = y_scale.length -
1;
// Don't draw outside of the frame.
g.setClip (frame_start_x+1, frame_start_y+1,
frame_width-1, frame_height-1);
// Check if ready to draw the line
if (fParameters == null) return;
// Get conversion factors from data
scale to frame pixels
double x_scale_factor = fParameters[0]/frame_width;
double y_scale_factor = fParameters[1]/frame_height;
// Create our complex variable for
the algorithm.
Complex z = new Complex
(0.0,0.0);
Complex c = new Complex
(0.0,0.0);
// Coordinates in pixel scale
int row = 0;
int col = 0;
// Outer loop steps through the c
value along the horizontal axis.
for (row = 0; row < frame_height;
row++) {
// Change
from pixel scale to the data scale
double y_beg
= y_scale[y_max_index] - row * y_scale_factor;
for (col =
0; col < frame_width; col++) {
double
x_beg = x_scale[0] + col * x_scale_factor;
//
Create the c for this point on the complex plane
c.real
= x_beg; c.img = y_beg; // Set the complex constant
z.real
= 0.0; z.img = 0.0; // Initialize
z to 0 + 0i.
boolean
escaped = false;
int
iter = 0;
double
x = x_beg;
double
y = y_beg;
double
mag = 0.0;
//
Iterate the function:
//
z = z*z + c
do
{
z.multiply
(z);
z.add
(c);
mag
= z.modulus ();
escaped
= mag > ESCAPE_MAGNITUDE;
++iter;
}
while (iter < MAX_ITERATIONS && (!escaped));
//
If escaped, then set color to a shade of gray
//
proportional to the number of interations. The
//
more iterations, the darker
if
(escaped) {
int
gray = 0xFF - (0xFF*iter)/MAX_ITERATIONS;
gray
= Math.min (gray, 240);
g.setColor
(new Color (gray, gray, gray));
g.drawLine
(col+frame_start_x, row+frame_start_y,
col+frame_start_x,
row+frame_start_y);
}
else {
//
Did not escape so relate the color instead to the
//
final magnitude of z.
int
i = ((int)(100*mag)) / ESCAPE_MAGNITUDE + 1;
int
red = (100*i) & 0xFF;
int
grn = (150*i) & 0xFF;
int
blu = (200*i) & 0xFF;
g.setColor
(new Color (red,grn,blu));
//
Use the drawLine method to plot a point.
g.drawLine
(col+frame_start_x, row+frame_start_y,
col+frame_start_x,
row+frame_start_y);
}
}
}
g.setColor (save_color);
} // draw
} // class DrawMandlebrot
|
Most recent update: June 25, 2005
|