Julia
/********************************************************************
* *
* Julia.java By Mark McClure - Feb 1996 *
* mmcclure@liberty.uc.wlu.edu *
* http://liberty.uc.wlu.edu/~mmcclure/java/Julia/ *
* *
* This applet generates julia sets for the quadratic family of *
* functions f(z) = z^2 + c. The complex parameter c may be *
* chosen from am image of the Mandelbrot set or entered into *
* a TextField. *
* *
********************************************************************/
// Need some tools.
import java.awt.*;
import java.util.StringTokenizer;
// -------------------> The Julia class <------------------------
// This is the main class which extends the Applet class and
// implements the Runnable interface to run smoothly.
public class Julia extends java.applet.Applet implements Runnable{
static final int H_SIZE = 600; // Horizontal size of the applet
static final int V_SIZE = 330; // Vertical size of the applet
static Thread initRunner; // Initialization Thread
JuliaCanvas juliaCanvas; // Place to draw the Julia Set
MandelCanvas mandelCanvas; // Place to draw the Madelbrot Set
ControlPanel p; // Holds The controls
Image mandel_img; // The Mandelbrot set displayed by the applet
// is a gif rather than a generated image.
public void init() {
// Initialization
resize(H_SIZE, V_SIZE);
setFont(new Font("TimesRoman", Font.PLAIN, 12));
setBackground(Color.white);
setForeground(Color.black);
setLayout(new BorderLayout());
// The two main canvases - one for the Julia set and one
// for the Mandelbrot set.
juliaCanvas = new JuliaCanvas(H_SIZE/2);
mandel_img = getImage(getCodeBase(), "mandel.gif");
mandelCanvas =
new MandelCanvas(mandel_img, juliaCanvas, p, H_SIZE/2);
// A panel to hold the two main canvases.
Panel innerPanel = new Panel();
add("Center", innerPanel);
innerPanel.setLayout(new GridLayout(1, 2, 2, 2));
innerPanel.add(mandelCanvas);
innerPanel.add(juliaCanvas);
// The control panel.
p = new ControlPanel(juliaCanvas, mandelCanvas);
p.resize(400, 30);
add("South", p);
show();
} //end init()
// Applet is computationally intensive.
// Definately needs a thread.
public void start(){
if(initRunner == null) {
initRunner = new Thread(this);
initRunner.setPriority(Thread.MIN_PRIORITY);
initRunner.start();
}
} //end start()
// Clean up when applet stops.
public synchronized void stop() {
if( juliaCanvas.juliaRunner != null ) {
juliaCanvas.juliaRunner.stop();
}
juliaCanvas.juliaRunner = null;
if(mandelCanvas.canvasRunner != null ) {
mandelCanvas.canvasRunner.stop();
}
mandelCanvas.canvasRunner = null;
if( initRunner != null ) {
initRunner.stop();
}
initRunner = null;
//destroy();
} //end stop()
// Instantiating each complex number for the Julia Set takes
// time and happens in this initialization thread.
public void run() {
int i;
for(i=0; i<juliaCanvas.num_points; i++) {
// Set the complex numbers out of the picture
juliaCanvas.points[i] = new ComplexNumber(5);
}
}
}//end Julia class.
// -----------------> The Mandel Canvas class <----------------
// This class holds all the information relevant to the displayed
// Mandelbrot set.
class MandelCanvas extends Canvas {
Image mandel_img; // Holds the Mandelbrot gif
JuliaCanvas juliaCanvas; // Link to the JuliaCanvas
ControlPanel p; // Link to the ControlPanel
Thread canvasRunner; // A thread for the JuliaCanvas
boolean update_positionQ = true; // Only want to update the position
// display until a point is selected.
// Bounds for the Mandelbrot Set in the complex plane
static final double z_left = -2;
static final double z_right = .6;
static final double z_top = 1.3;
static final double z_bot = -1.3;
int pixel_size; // Size of the Mandelbrot image in pixels
// The constructor method
MandelCanvas(Image mandel_passed, JuliaCanvas juliaCanvas_passed,
ControlPanel p_passed, int pixel_size_passed) {
// Set these variables to the values passed
mandel_img = mandel_passed;
juliaCanvas = juliaCanvas_passed;
p = p_passed;
pixel_size = pixel_size_passed;
// Set some stuff
setBackground(Color.white);
show();
}// end constructor method
// Update the postion display when the mouse moves.
public boolean mouseMove(Event evt, int x, int y) {
if(update_positionQ) {
ComplexNumber c = complexConvert(x,y);
p.update_position(c);
}
return true;
}
// Clear the postion display when the mouse moves.
public boolean mouseExit(Event evt, int x, int y) {
if(update_positionQ) {
p.positionDisplay.setText("");
}
return true;
}
// The action starts when the user clicks on the Mandelbrot set.
public boolean mouseUp(Event evt, int x, int y) {
ComplexNumber c = complexConvert(x,y); // Need a complex
// number from (x,y)
// Update the position display for the last time.
p.update_position(c);
update_positionQ = false;
// Set the Julia parameter and begin the computation thread.
juliaCanvas.c = c;
if( canvasRunner == null ) {
canvasRunner = new Thread(juliaCanvas);
canvasRunner.setPriority(Thread.MIN_PRIORITY);
canvasRunner.start();
}
if( !canvasRunner.isAlive() ) {
canvasRunner.stop();
canvasRunner = new Thread(juliaCanvas);
canvasRunner.setPriority(Thread.MIN_PRIORITY);
canvasRunner.start();
}
return true;
} //end mouseUp()
// Called by mouseUp() to converty (x,y) to a complex number.
ComplexNumber complexConvert(int x, int y) {
double a, b;
a = (double) ( ((z_right - z_left)/pixel_size)*x + z_left );
b = (double) ( ((z_bot - z_top)/pixel_size)*y + z_top );
return new ComplexNumber(a,b);
}
public void paint(Graphics g) {
g.drawImage(mandel_img, 0, 0, this);
}
}//end MandelCanvas class
// ------------------> The JuliaCanvas class <-----------------
// This class holds the information needed to draw the Julia Set.
// The set itself is an object of class JuliaSet.
// Implents runnable since drawing is time consuming.
class JuliaCanvas extends Canvas implements Runnable {
static ComplexNumber c; // Parameter defining the Julia Set
static final int num_points = 5000; // Number of points to draw
static final double z_size = 4.0; // Size of the set in complex plane
static int pixel_size; // Size of the set in pixels
static Thread juliaRunner; // Thread to start() theSet in
JuliaSet theSet; // The set itself of class JuliaSet
// An array to hold the complex numbers in the Julia Set
ComplexNumber points[] = new ComplexNumber[num_points];
// The constructor method
JuliaCanvas(int pixel_size_passed) {
// Set these variables to the values passed
theSet = new JuliaSet(num_points, points, this, getGraphics());
pixel_size = pixel_size_passed;
// Set some stuff
setBackground(Color.white);
setForeground(Color.black);
setFont(new Font("TimesRoman", Font.PLAIN, 12));
resize(pixel_size, pixel_size);
show();
}// end constructor method
public void run() {
theSet.c = c; // Sets the parameter
startJulia(); // Starts the computation of the Julia Set.
}
// Called by run to compute the set.
void startJulia() {
if(juliaRunner == null) {
juliaRunner = new Thread(theSet);
juliaRunner.setPriority(Thread.MIN_PRIORITY);
juliaRunner.start();
}
if( !juliaRunner.isAlive() ) {
juliaRunner.stop();
juliaRunner = new Thread(theSet);
juliaRunner.setPriority(Thread.MIN_PRIORITY);
juliaRunner.start();
}
}// end startJulia()
// Called in response to a button press to clear the canvas.
public void clear() {
int i;
for(i=0; i<num_points; i++) {
// Set the complex numbers out of the picture
points[i] = new ComplexNumber(5);
}
repaint();
}
// Plots the complex numbers that form the Julia Set.
void plot(ComplexNumber z) {
int x,y;
Graphics g = getGraphics();
x = (int) ( (pixel_size/z_size)*(z.a + z_size/2) );
y = (int) ( (pixel_size/z_size)*(z_size/2 - z.b) );
g.drawLine(x,y, x,y);
}
public void paint(Graphics g) {
int i;
// Plot the JuliaSet. Check first to make sure that we
// need to plot it. If the first point (theSet.points[1])
// has real part > 4 this must be because the image has
// been cleared.
if( (!juliaRunner.isAlive()) && (juliaRunner != null)
&& (theSet.points[1].a < 4) ) {
for(i=0; i < num_points; i++) {
plot(theSet.points[i]);
}
}
}
}//end JuliaCanvas class
// -------------> The JuliaSet class <----------------
// Holds the information defining the Julia Set itself.
class JuliaSet implements Runnable {
ComplexNumber c; // Set by the function JuliaCanvas.run()
int num_points; // Number of points to draw
ComplexNumber points[]; // Points in the JuliaSet
JuliaCanvas canvas; // A place to draw the JuliaSet
Graphics g; // Defines the drawing functions
// The constructor method
JuliaSet(int num_points_passed, ComplexNumber[] points_passed,
JuliaCanvas canvas_passed, Graphics g_passed) {
// Set these variables to the values passed
num_points = num_points_passed;
points = points_passed;
canvas = canvas_passed;
g = g_passed;
}// end constructor method
// This is the main computational loop and is, therfore, run in
// a thread.
public void run() {
int i;
double a = Math.random();
double b = Math.random();
points[0] = new ComplexNumber(a, b);
for(i=0; i < num_points - 1; i++) {
points[i+1] = fc( (int) (2*Math.random()), points[i]);
canvas.plot(points[i]);
}
}
// Iteration of this function generates the Julia Set.
ComplexNumber fc(int choice, ComplexNumber z) {
if(choice == 0) {
return z.minus(c).sqrt();
}
else {
return z.minus(c).sqrt().ominus();
}
}
}//end JuliaSet class
// -----------> The ComplexNumber class <----------------
// The computations above are in terms of complex numbers
// defined here.
class ComplexNumber {
double a, b; // The real and imaginary parts
// ---> Several constructor Methods <---
ComplexNumber(double a_passed, double b_passed) {
a = a_passed;
b = b_passed;
}
ComplexNumber(double a_passed) {
a = a_passed;
b = 0.0;
}
ComplexNumber() {
a = 0.0;
b = 0.0;
}// end constructor methods
// ---> Several arithmetic methods <---
ComplexNumber plus(ComplexNumber z) {
return new ComplexNumber(a + z.a, b + z.b);
}
ComplexNumber minus(ComplexNumber z) {
return new ComplexNumber(a - z.a, b - z.b);
}
ComplexNumber ominus() {
return new ComplexNumber(-a, -b);
}
//The only slightly tricky operation
ComplexNumber sqrt() {
// The polar components of the number
double theta;
double r = Math.sqrt(Math.sqrt(a*a + b*b));
// The cartesian components of the square root
double a_sqrt, b_sqrt;
// Compute theta
if(a >= 0) {
theta = (Math.atan(b/a)/2);
}
else {
if(b >= 0) {
theta = (Math.atan(b/a) + Math.PI)/2;
}
else {
theta = (Math.atan(b/a) - Math.PI)/2;
}
}// end computation of theta
// Compute the cartesian components of the square root
a_sqrt = r*(Math.cos(theta));
b_sqrt = r*(Math.sin(theta));
return new ComplexNumber(a_sqrt, b_sqrt);
}// end sqrt()
// end arithmetic methods
}// end ComplexNumber class
// ------------> The ControlPanel class <------------------
//This class defines the ControlPanel and responds to events.
class ControlPanel extends Panel {
Panel mandelPanel;
Panel juliaPanel;
Button stop_button; // The stop button
Button clear_button; // The clear button
JuliaCanvas juliaCanvas; // Linked to the JuliaCanvas
MandelCanvas mandelCanvas; // Linked to the MandelCanvas
static TextField positionDisplay; // Displays the Julia parameter
Thread juliaTextRunner; // The user may start the
// computation by entering a
// value in the TextField. So
// we need a thread for that
// computation
// The constructor method.
ControlPanel(JuliaCanvas juliaCanvas_passed,
MandelCanvas mandelCanvas_passed) {
// Set the layout
try{
setLayout(new GridLayout(1, 2, 0, 0));
}
catch(IllegalArgumentException e){/* Ignore exception */}
// I'm not even sure why this
// exception exists.
// Place a panel below the Mandelbrot image and the Julia image.
mandelPanel = new Panel();
juliaPanel = new Panel();
add(mandelPanel);
add(juliaPanel);
// Create and add the position display to the mandelPanel.
positionDisplay = new TextField(25);
positionDisplay.setEditable(true);
mandelPanel.add(positionDisplay);
// Create and add the buttons to the juliaPanel.
stop_button = new Button("Stop");
clear_button = new Button("Clear");
juliaPanel.add(stop_button);
juliaPanel.add(clear_button);
// Set the canvases to the values passed.
juliaCanvas = juliaCanvas_passed;
mandelCanvas = mandelCanvas_passed;
// Set the background color
setBackground(Color.gray);
}// end constructor method
//The event handler.
public boolean action(Event evt, Object arg) {
// Figure out which Button is pressed.
if (evt.target instanceof Button) {
choose( (String) arg);
}
// Start computation if user hits return in the TextField
if( (evt.target instanceof TextField)
&& (evt.id == Event.ACTION_EVENT) ) {
// Seems that I should have to check to see if this works.
juliaCanvas.c = parseInput(positionDisplay.getText());
if( juliaTextRunner == null ) {
juliaTextRunner = new Thread(juliaCanvas);
juliaTextRunner.setPriority(Thread.MIN_PRIORITY);
juliaTextRunner.start();
}
if( !juliaTextRunner.isAlive() ) {
juliaTextRunner.stop();
juliaTextRunner = new Thread(juliaCanvas);
juliaTextRunner.setPriority(Thread.MIN_PRIORITY);
juliaTextRunner.start();
}
}// end if
return true;
}// end action()
//Called by action in response to the Button press event.
void choose(String button_name) {
if(button_name.equals("Stop"))
juliaCanvas.juliaRunner.stop();
else {
mandelCanvas.update_positionQ = true;
juliaCanvas.juliaRunner.stop();
juliaCanvas.clear();
juliaCanvas.repaint();
}
}// end choose
// Called by MandelCanvas.mouseEvents to update the postion.
public static void update_position(ComplexNumber c) {
String x = new Double(c.a).toString();
if(c.b >= 0) {
String y = new Double(c.b).toString();
positionDisplay.setText("c = " + x + " + " + y + " i");
}
else {
String y = new Double(-c.b).toString();
positionDisplay.setText("c = " + x + " - " + y + " i");
}
}// end choose
// When user presses return in the TextField, we want to compute the
// JuliaSet. So we need to parse the TextField to get the complex
// Juliaset parameter. The user **must** enter text in the format
// "c = a + b i" or "c = a - b i" where 'a' and 'b' are non-negative
// numbers.
ComplexNumber parseInput(String str) {
// Use the StringTokenizer to pares the TextField.
StringTokenizer string_toke = new StringTokenizer(str, " ");
int num_tokes = string_toke.countTokens();
// User may enter "c = a + b i" or "c = a - b i".
// plus_or_minus tells which one it was.
String plus_or_minus;
// -------> Hopefully I can delete this soon <-----------
// There's a wierd Netscape bug (PowerMac only) which won't let
// the user type capital letters (or + which is -shift- =) into
// a text field. wierd_bugQ helps deal with this.
boolean wierd_bugQ = false;
double a = 0, b = 0; // The real and imaginary parts
int i;
// Check to make sure the text was entered correctly
if( num_tokes != 6 ) {
inputError();
}
if( !string_toke.nextToken().equalsIgnoreCase("c") ) {
inputError();
}
if( !string_toke.nextToken().equals("=") ) {
inputError();
}
// Get the real part.
try {
a = new Double(string_toke.nextToken()).doubleValue();
}
catch(NumberFormatException e) {
inputError();
}
// Get the imaginary part. Check whether it's positve or
// negative first.
plus_or_minus = new String(string_toke.nextToken());
if( !( plus_or_minus.equals("+") || plus_or_minus.equals("-") ) ) {
// ---------> Hopefully this goes away soon <--------
if( plus_or_minus.equals("=") ) {
wierd_bugQ = true;
}
else {
inputError(); // would need to keep this
}
}
try {
b = new Double(string_toke.nextToken()).doubleValue();
}
catch(NumberFormatException e) {
inputError();
}
if(plus_or_minus.equals("-")) {
b = -b;
}
// Check for the 'i'.
if( !string_toke.nextToken().equalsIgnoreCase("i") ) {
inputError();
}
// The wierd bug occurs if the user tried to type + on
// a PowerMac. This resets the display correctly.
if( wierd_bugQ == true ) {
positionDisplay.setText("c = "+a+" + "+b+" i");
}
// Return the ComplexNumber.
return new ComplexNumber(a,b);
}// end parseInput()
// Display message if input is incorrectly formatted.
static void inputError() {
positionDisplay.setText("Input Error");
}
}//end ControlPanel class.
//end file
Back to the Julia applet page
New on the Java Boutique:
New Review:
Time Management Made Easy with the Quartz Enterprise Job Scheduler
Why not just use the Java timer API? This open source scheduling
API boasts simplicity, ease-of-integration, a well-rounded feature
set, and it's free!
New Applet:
Reverse Complement
Reverse Complement is a simple applet that converts DNA or RNA
sequences into three useful formats.
Elsewhere on internet.com:
WebDeveloper Java
Lots of Java information on webdeveloper.com
WDVL Java
Thorough Java resource at the Web Developer's Virtual Library.
ScriptSearch Java
Hundreds of free Java code files to download.
jGuru: Your View of the Java Universe
Customizable portal with online training, FAQs, regular news updates, and tutorials.