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

How to Add Java Applets to Your Site

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.