Buddhabrot (java)

De GirinoWiki

Por ser mais complexa que as outras, esta applet foi dividida em diversas classes, num modelo MVC usando os Padrões de Projeto de operadores. O pacote java utilizadopara as classes é o mesmo das outras implementações de fractais: org.girino.frac.

Aqui seguem as classes e um exemplo de HTML para usá-las:

Tabela de conteúdo

[editar] org.girino.frac.BuddhaBrotApplet.java

package org.girino.frac;
 
 
import java.applet.Applet;
import java.awt.Button;
import java.awt.Checkbox;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.Panel;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.text.NumberFormat;
import java.util.Calendar;
import java.util.Properties;
 
import javax.imageio.ImageIO;
 
import org.girino.frac.operators.InverseBuddhaOperator;
 
/**
 * Applet to display BuddhaBrot sets. This applet
 * is only the view part of the system. Can be used with different
 * engines to display different fractals.
 * 
 * The most important method is drawCurrentState(BuddhaOperator model);
 * This is what is called by the controler.
 * 
 * @author girino
 *
 */
public class BuddhaBrotApplet extends Applet {
 
	/**
	 * This can be used to adjust the apearance of the buddha. values of 0.3-0.5
	 * tends to be more buddha like. Above that very little detail is visible. 
	 * Bellow this, too much detail is shown and it looks more like a big ball. 
	 */
	private static final double COLOR_EXPONENT_BLUE = 0.5;
	private static final double COLOR_EXPONENT_GREEN = 0.5;
	private static final double COLOR_EXPONENT_RED = 0.5;
	/**
	 * I was just testing changes in color... 
	 */
	public static final boolean USE_MATH_POW = true;
 
	// Parameters
	private int viewSize;
	private double scale;
	private BuddhaBrotControler controler;
 
	// Running support
	private Properties defaultParameters = new Properties();
	private BufferedImage image;
	private Panel imagePanel;
 
	// checkboxes for redrawing
	private Checkbox stopRed = new Checkbox("Stop Red");
	private Checkbox stopGreen = new Checkbox("Stop Green");
	private Checkbox stopBlue = new Checkbox("Stop Blue");
 
	/**
	 * Asks controler to start redraw thread. or whatever 
	 * @see Applet#start()
	 */
	public void start() {
		controler.startRedraw();
	}
 
	/**
	 * Asks controler to stop redraw thread. or whatever 
	 * @see Applet#stop()
	 */
	public void stop() {
		controler.stopRedraw();
	}
 
	/**
	 * Inits parameters and creates controler.
	 * 
	 * I tryed to use MVC here so it would ease maintenance.
	 * The Applet is the View, the controler is the Controller (duh)
	 * And the operators are the model.
	 * 
	 *
	 */
	public BuddhaBrotApplet() {
		defaultParameters.put("tamanho", "800");
		defaultParameters.put("x0", "-0.28");
		defaultParameters.put("y0", "0");
		defaultParameters.put("escala", "2.5");
		defaultParameters.put("tempoRedesenho", "3000");
		defaultParameters.put("tempoAutoSave", "300000");
		defaultParameters.put("operator", "org.girino.frac.operators.InverseBuddhaOperator");
		//defaultParameters.put("operator", "org.girino.frac.operators.MonochromeBuddhaOperator");
		//defaultParameters.put("operator", "org.girino.frac.operators.RealInverseBuddhaOperator");
		//defaultParameters.put("dumpFileName","data/" + MonochromeBuddhaOperator.class.getName() + ".dmp");
		defaultParameters.put("dumpFileName","data/" + InverseBuddhaOperator.class.getName() + ".dmp");
		//defaultParameters.put("dumpFileName","data/" + MonochromeBuddhaOperator.class.getName() + ".dmp");
		//defaultParameters.put("dumpFileName","data/" + RealInverseBuddhaOperator.class.getName() + ".dmp");
 
		controler = new BuddhaBrotControler(this);
		//controler = new GridBuddhaBrotControler(this);
	}
 
	/**
	 * Overides parent to allow for default values.
	 * @see Applet#getParameter(java.lang.String)
	 */
	public String getParameter(String name) {
		String ret = null;
		try {
			ret = super.getParameter(name);
		} catch(NullPointerException e) {
			; // silently ignore
		}
		if (ret == null) {
			return getDefaultParameter(name);
		}
		return ret;
	}
 
	private String getDefaultParameter(String name) {
		return defaultParameters.getProperty(name);
	}
 
	/**
	 * inits applet, inits controler.
	 * 
	 * Create components and even listeners.
	 * 
	 * @see Applet#init()
	 */
	public void init() {
		Properties properties = getPropertiesFromApplet();
		initParameters(properties);
		initComponents();
		prepareEventListeners();
		controler.init();
	}
 
	/**
	 * stupid, but i thought it would work! Poor me...
	 * @return
	 */
	private Properties getPropertiesFromApplet() {
		Properties properties = new Properties(defaultParameters);
		String [][] params = this.getParameterInfo();
		for (int i = 0; params != null && i < params.length; i++) {
			System.out.println(params[i][0] + "," + params[i][1] + "," + params[i][2]);
			properties.put(params[i][0], params[i][1]);
		}
		return properties;
	}
 
	/**
	 * Inits awt components, creates panels and buttons, etc.
	 *
	 */
	private void initComponents() {
 
		this.setLayout(null);
 
		image = new BufferedImage(viewSize, viewSize, BufferedImage.TYPE_INT_RGB);
		imagePanel = new Panel() {
			public void paint(Graphics g) {
				g.drawImage(image, 0, 0, null);
			};
		};
		this.add(imagePanel);
		imagePanel.setLocation(0,0);
		imagePanel.setSize(viewSize, viewSize);
		Panel buttonPanel = new Panel();
		Button saveDataButton = new Button("Save data");
		saveDataButton.addActionListener(new ActionListener() {
			public void actionPerformed(java.awt.event.ActionEvent evt) {
				try {
					controler.dumpToFile();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			};
		});
		Button saveImageButton = new Button("Save image");
		saveImageButton.addActionListener(new ActionListener() {
			public void actionPerformed(java.awt.event.ActionEvent evt) {
				try {
					saveImage();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			};
		});
		final Button stopStartButton = new Button("Stop");
		stopStartButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				if (controler.isRunningStopped()) {
					stopStartButton.setEnabled(false);
					controler.startAll();
					stopStartButton.setLabel("Stop");
					stopStartButton.setEnabled(true);
				} else {
					stopStartButton.setEnabled(false);
					controler.stopAll();
					stopStartButton.setLabel("Start");
					stopStartButton.setEnabled(true);
				}
			}
		});
 
		buttonPanel.setLayout(new GridLayout(1,6));
		buttonPanel.add(saveDataButton);
		buttonPanel.add(saveImageButton);
		buttonPanel.add(stopStartButton);
		buttonPanel.add(stopRed);
		buttonPanel.add(stopGreen);
		buttonPanel.add(stopBlue);
		this.add(buttonPanel);
		buttonPanel.setLocation(0,viewSize);
		buttonPanel.setSize(viewSize, 20);
 
		resize(viewSize,viewSize+20);
	}
 
	/**
	 * Save current displayed image as png. Uses date to form file name.
	 * @throws IOException
	 */
	protected void saveImage() throws IOException {
		String filename = getNewImageName();
		System.err.println("Saving: " + filename);
		ImageIO.write(image, "png", new File(filename));
	}
 
	/**
	 * Creates a new image name based on current date and time.
	 * @return
	 */
	private String getNewImageName() {
		NumberFormat f2 = NumberFormat.getInstance();
		f2.setMaximumIntegerDigits(2);
		f2.setMinimumIntegerDigits(2);
		f2.setMaximumFractionDigits(0);
		NumberFormat f4 = NumberFormat.getInstance();
		f4.setMaximumIntegerDigits(4);
		f4.setMinimumIntegerDigits(4);
		f4.setMaximumFractionDigits(0);
		f4.setGroupingUsed(false);
		Calendar now = Calendar.getInstance();
 
		String ret = "data/";
		ret += f4.format(now.get(Calendar.YEAR));
		ret += f2.format(now.get(Calendar.MONTH)+1);
		ret += f2.format(now.get(Calendar.DAY_OF_MONTH));
		ret += f2.format(now.get(Calendar.HOUR_OF_DAY));
		ret += f2.format(now.get(Calendar.MINUTE));
		ret += f2.format(now.get(Calendar.SECOND));
		ret += ".png";
 
		return ret;
	}
 
	/**
	 * Most parameters were migrated to controler and model.
	 * Only "tamanho" (screen size) and "escala" (scale) where kept here.
	 * 
	 * @param p
	 */
	private void initParameters(Properties p) {
		viewSize = Integer.valueOf(p.getProperty("tamanho")).intValue();
		scale=(double)viewSize/Double.valueOf(p.getProperty("escala")).doubleValue();
	}
 
	/**
	 * Creates event listeners for... huh... 
	 * this shouldn't be here... its not used anymore
	 *
	 */
	private void prepareEventListeners() {
 
		addMouseListener(new MouseAdapter() {
 
			public void mouseClicked(MouseEvent ev) {
				try {
					controler.dumpToFile();
				} catch (IOException e) {
					throw new RuntimeException(e);
				}
			}
 
		});
	}
 
	/**
	 * fun part for the view...
	 * 
	 * draws an image with current state of the set.
	 * 
	 * @param view
	 */
	public void drawCurrentState(BuddhaOperator view) {
		long[][] img_red = view.getRedImage();
		long[][] img_green = view.getGreenImage();
		long[][] img_blue = view.getBlueImage();
		for(int j = 0; j < viewSize; j++) {
			for (int i = 0; i < viewSize; i++) {
				float red = 0f;
				float green = 0f;
				float blue = 0f;
				if (USE_MATH_POW) {
					red = (stopRed.getState())?0f:Math.min((float)Math.pow(img_red[j][i]/(float)view.getMaxRed(), COLOR_EXPONENT_RED), 1);
					green = (stopGreen.getState())?0f:Math.min((float)Math.pow(img_green[j][i]/(float)view.getMaxGreen(), COLOR_EXPONENT_GREEN), 1);
					blue = (stopBlue.getState())?0f:Math.min((float)Math.pow(img_blue[j][i]/(float)view.getMaxBlue(), COLOR_EXPONENT_BLUE), 1);
				} else {
					red = (stopRed.getState())?0f:Math.min(img_red[j][i]/(float)view.getMaxRed(), 1);
					green = (stopGreen.getState())?0f:Math.min(img_green[j][i]/(float)view.getMaxGreen(), 1);
					blue = (stopBlue.getState())?0f:Math.min(img_blue[j][i]/(float)view.getMaxBlue(), 1);
				}
				Color c = new Color(red, green, blue);
				image.setRGB(i,j,c.getRGB());
				if (controler.isRedrawStopped()) {
					return;
				}
			}
		}
		Graphics g = imagePanel.getGraphics();
		g.drawImage(image, 0, 0, null);
	}
 
	public double rescaleX(double xf) {
    	return (xf-viewSize/2)/scale + controler.getX();
	}
 
    public double rescaleY(double yf)	{
    	return (yf-viewSize/2)/scale + controler.getY();
    }
 
    public double revertScaleX(double xf) {
    	return (xf-controler.getX())*scale + viewSize/2;
	}
 
	public double revertScaleY(double yf)	{
    	return (yf-controler.getY())*scale + viewSize/2;
    }
 
	public static void main(String[] args) {
		BuddhaBrotApplet applet = new BuddhaBrotApplet();
		applet.init();
		applet.start();
		applet.stop();
		runPrompt(applet);
	}
 
	private static void runPrompt(BuddhaBrotApplet applet) {
		BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
		while (true) {
			System.out.println("Valid commands: quit");
			System.out.print("> ");
			String line = null;
			try {
				line = in.readLine();
			} catch (IOException e) {
				; // silently ignore errors.
			}
			if (line != null && line.equalsIgnoreCase("quit")) {
				break;
			} else {
				System.out.println("Error: Invalid command.");
			}
		}
		applet.controler.stopAll();
		try {
			applet.controler.dumpToFile();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
 
	public double getScale() {
		return scale;
	}
 
	public void setScale(double scale) {
		this.scale = scale;
	}
 
	public int getViewSize() {
		return viewSize;
	}
 
	public void setViewSize(int size) {
		this.viewSize = size;
	}
 
}

[editar] org.girino.frac.BuddhaBrotControler.java

package org.girino.frac;
 
import java.io.IOException;
 
import org.girino.frac.operators.NormalBuddhaOperator;
 
/**
 * This class is the controler part of the buddhabrot system.
 * It creates threads for updating screens, 
 * autosaving and also for doing the calculations.
 * 
 * The logic is kept here (except for the formulas, which is
 * considered as model and is thus in the Operator).
 * 
 * @author girino
 *
 */
public class BuddhaBrotControler {
 
	private boolean runningStopped = false;
	private boolean redrawStopped = false;
	private boolean savingStopped = false;
	private Thread runnerThread = null;
	private Thread repaintThread = null;
	private Thread savingThread = null;
	// must rename to model.
	private BuddhaOperator operator;
	private BuddhaBrotApplet view;
	// initial coordinates
	private double x;
	private double y;
	// times
	private int savingTime = 300000;
	private int redrawTime = 4000;
 
 
	/**
	 * Creates and keeps the view for future use.
	 * 
	 * @param view
	 */
	public BuddhaBrotControler(BuddhaBrotApplet view) {
		this.view = view;
	}
 
	/**
	 * Starts All the threads.
	 *
	 */
	public void startAll() {
		startRunning();
		startRedraw();
		startAutoSave();
	}
 
	/**
	 * Starts autosave threads.
	 *
	 */
	public void startAutoSave() {
		savingStopped = false;
		createSavingThread();
		System.err.println("startAutoSave");
	}
 
	/**
	 * Starts redraw threads.
	 *
	 */
	public void startRedraw() {
		redrawStopped = false;
		createRedrawThread();
		System.err.println("startRedraw");
	}
 
	/**
	 * starts calculation threads.
	 *
	 */
	public void startRunning() {
		runningStopped = false;
		createRunnerThread();
		System.err.println("startRunning");
	}
 
	/**
	 * stop all threads.
	 *
	 */
	public void stopAll() {
		// asks them to finish beforehand
		runningStopped = redrawStopped = savingStopped = false;
		stopRunning();
		stopRedraw();
		stopAutoSave();
	}
 
	/**
	 * stop redraw threds.
	 *
	 */
	public void stopRedraw() {
		redrawStopped = true;
		if (repaintThread != null) {
			try {
				repaintThread.join();
			} catch (InterruptedException e) {
			}
		}
		repaintThread = null;
		System.err.println("stopRedraw");
	}
 
	/**
	 * stop calculation threads.
	 *
	 */
	public void stopRunning() {
		runningStopped = true;
		if (runnerThread != null) {
			try {
				runnerThread.join();
			} catch (InterruptedException e) {
			}
		}
		runnerThread = null;
		System.err.println("stopRunning");
	}
 
	/**
	 * stop autosave threads.
	 *
	 */
	public void stopAutoSave() {
		savingStopped= true;
		if (savingThread != null) {
			try {
				savingThread.join();
			} catch (InterruptedException e) {
			}
		}
		savingThread = null;
		System.err.println("stopAutoSave");
	}
 
	/**
	 * Create sthe thread that is going to control the calculation.
	 * The thread is an anonymous inner class for convenience.
	 *
	 */
	private void createRunnerThread() {
		if(runnerThread == null) {
			runningStopped = false;
			runnerThread = new Thread(new Runnable() {
				public void run() {
					while (!runningStopped) {
						nextpoints();
					}
				}
			});
			runnerThread.start();
		}
	}
 
	/**
	 * Create sthe thread that is going to control the screen redrawing.
	 * The thread is an anonymous inner class for convenience.
	 * parameter "tempoRedesenho" is used to calculate the interval between
	 * redraws.
	 *
	 */
	private void createRedrawThread() {
		if(repaintThread == null) {
			redrawStopped = false;
			repaintThread = new Thread(new Runnable() {
				public void run() {
					view.drawCurrentState(operator);
					while (!redrawStopped) {
						try {
							Thread.sleep(redrawTime);
							view.drawCurrentState(operator);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
					}
				}
			});
			repaintThread.start();
 
		}
	}
 
	/**
	 * Create sthe thread that is going to control the auto saving.
	 * The thread is an anonymous inner class for convenience.
	 * parameter "tempoAutoSave" is used to calculate the interval between
	 * savings.
	 */
	private void createSavingThread() {
		if(savingThread == null) {
			savingStopped = false;
			savingThread = new Thread(new Runnable() {
				public void run() {
					while (!savingStopped) {
						try {
							Thread.sleep(savingTime);
							operator.dumpToFile("data/" + operator.getClass().getName() + ".dmp");
						} catch (InterruptedException e) {
							e.printStackTrace();
						} catch (IOException e) {
							e.printStackTrace();
						}
					}
				}
			});
			savingThread.start();
		}
	}
 
	/**
	 * initializes the controler and starts the threads.
	 *
	 */
	public void init() {
		initParameters();
		startAll();
	}
 
	private void recoverDumpedOperator(String fileName) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException {
		BuddhaOperator dummy = new NormalBuddhaOperator();
		operator = dummy.loadFromFile(fileName);
	}
 
	private void createOperator(String operatorName) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
		operator = (BuddhaOperator)Class.forName(operatorName).newInstance();
		operator.init(view);
	}
 
 
	/**
	 * Reads parameters fro mthe view (must change that).
	 *
	 */
	public void initParameters() {
		x = Double.valueOf(view.getParameter("x0")).doubleValue();
		y = Double.valueOf(view.getParameter("y0")).doubleValue();
		redrawTime = Integer.valueOf(view.getParameter("tempoRedesenho")).intValue();
		savingTime = Integer.valueOf(view.getParameter("tempoAutoSave")).intValue();
 
		if (view.getParameter("dumpFileName") != null) {
			try {
				recoverDumpedOperator(view.getParameter("dumpFileName"));
			} catch (Exception e) {
				; //silently ignores
				System.err.println("Could not open file: " + view.getParameter("dumpFileName"));
			}
		} 
		if (operator == null) {
			try {
				createOperator(view.getParameter("operator"));
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
		}
	}
 
	/**
	 * Calculates (and keeps calculating forever until stoped)
	 * the buddahbrot set.
	 * 
	 * This algorithm uses random points. A grid can be also used in 
	 * other implementations
	 * @see GridBuddhaBrotControler#nextpoints() 
	 *
	 */
	protected void nextpoints() {
 
		int[][] img_cache = new int[operator.getMaxIterations()][2];
		double multiplier = 4 * (double)view.getViewSize()/(double)view.getScale();
 
        while (!runningStopped) {
 
        	double a = (Math.random() - 0.5) * multiplier + x;
        	double b = (Math.random() - 0.5) * multiplier + y;
 
        	// optimization from http://en.wikipedia.org/wiki/User:Evercat/Buddhabrot.c
            if ((a >  -1.2 && a <=  -1.1 && b >  -0.1 && b < 0.1)
            		|| (a >  -1.1 && a <=  -0.9 && b >  -0.2 && b < 0.2)
            		|| (a >  -0.9 && a <=  -0.8 && b >  -0.1 && b < 0.1)
            		|| (a > -0.69 && a <= -0.61 && b >  -0.2 && b < 0.2)
            		|| (a > -0.61 && a <=  -0.5 && b > -0.37 && b < 0.37)
            		|| (a >  -0.5 && a <= -0.39 && b > -0.48 && b < 0.48)
            		|| (a > -0.39 && a <=  0.14 && b > -0.55 && b < 0.55)
            		|| (a >  0.14 && a <   0.29 && b > -0.42 && b < -0.07)
            		|| (a >  0.14 && a <   0.29 && b >  0.07 && b < 0.42)) {
            	continue;
            }
 
            double aa = a;
            double bb = b;
 
            int iter = 0;
            double mag2 = 0;;
            do {
				double aanew = operator.calcReal(aa, bb, a , b);
				double bbnew = operator.calcImaginary(aa, bb, a, b);
 
				aa = aanew;
				bb = bbnew;
				mag2 = aa*aa + bb*bb;
 
				double pointx = view.revertScaleX(aa);
				double pointy = view.revertScaleY(bb);
 
				img_cache[iter][0] = (int)Math.floor(pointx);
				img_cache[iter][1] = (int)Math.floor(pointy);
				iter++;
			} while ( (iter < operator.getMaxIterations()) && (mag2 < 4) );
            operator.apply(iter, mag2, img_cache);
        }  
	}
 
	/**
	 * saves the current state (actually saves the operator);
	 * 
	 * @throws IOException
	 */
	public void dumpToFile() throws IOException {
		operator.dumpToFile("data/" + operator.getClass().getName() + ".dmp");
	}
 
	/**
	 * gets the coordinates for the center of the screen.
	 * @return x
	 */
	public double getX() {
		return x;
	}
 
	/**
	 * gets the coordinates for the center of the screen.
	 * @return y
	 */
	public double getY() {
		return y;
	}
 
	/**
	 * checks if redraw is stoped.
	 * @return
	 */
	public boolean isRedrawStopped() {
		return redrawStopped;
	}
 
	/**
	 * checks if calculation is stoped.
	 * @return
	 */
	public boolean isRunningStopped() {
		return runningStopped;
	}
 
	/**
	 * checks if autosave is stoped.
	 * @return
	 */
	public boolean isSavingStopped() {
		return savingStopped;
	}
 
	/**
	 * means for subclasses to access the model.
	 * 
	 * @return
	 */
	protected BuddhaOperator getOperator() {
		return operator;
	}
 
	/**
	 * means for subclasses to access the view.
	 * 
	 * @return
	 */
	protected BuddhaBrotApplet getView() {
		return view;
	}
 
}

[editar] org.girino.frac.BuddhaOperator.java

package org.girino.frac;
 
import java.applet.Applet;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
 
/**
 * interface that provides all needed services for a buddha operator.
 * A buddha operator is the model (in this context, the formula and
 * the data holder for the image values) of the buddhabrot system.
 * @author girino
 *
 */
public interface BuddhaOperator extends Serializable {
	/**
	 * gets parameters from the applet.
	 * 
	 * @param parent
	 */
	public void init(Applet parent);
	/**
	 * gets the array of red points.
	 * @return
	 */
	public long[][] getRedImage(); 
	/**
	 * gets the array of green points.
	 * @return
	 */
	public long[][] getGreenImage(); 
	/**
	 * gets the array of blue points.
	 * @return
	 */
	public long[][] getBlueImage();
	/**
	 * sets a new array of red points.
	 * @param image
	 */
	public void setRedImage(long[][] image);
	/**
	 * sets a new array of green points.
	 * @param image
	 */
	public void setGreenImage(long[][] image);
	/**
	 * sets a new array of blue points.
	 * @param image
	 */
	public void setBlueImage(long[][] image);
	/**
	 * retuns the maximum value found for a red point.
	 * @return
	 */
	public long getMaxRed();
	/**
	 * retuns the maximum value found for a green point.
	 * @return
	 */
	public long getMaxGreen();
	/**
	 * retuns the maximum value found for a blue point.
	 * @return
	 */
	public long getMaxBlue();
	/**
	 * sets the right points in the images given an iteration from the controler.
	 * @param numIterations
	 * @param mag2
	 * @param cacheImage
	 */
	public void apply(int numIterations, double mag2, int[][] cacheImage);
	/**
	 * calculates the real part of the formula. 
	 * @param x
	 * @param y
	 * @param x0
	 * @param y0
	 * @return
	 */
	public double calcReal(double x, double y, double x0, double y0);
	/**
	 * Calculates the imaginary part of the formula.
	 * @param x
	 * @param y
	 * @param x0
	 * @param y0
	 * @return
	 */
	public double calcImaginary(double x, double y, double x0, double y0);
	/**
	 * returns the maximum number of iterations needed for thsi formula.
	 * @return
	 */
	public int getMaxIterations();
	/**
	 * dumps this data to file.
	 * 
	 * @param string
	 * @throws IOException
	 */
	public void dumpToFile(String string) throws IOException;
	/**
	 * load a new model from file.
	 * @param filename
	 * @return
	 * @throws IOException
	 * @throws ClassNotFoundException
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 */
	public BuddhaOperator loadFromFile(String filename) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException;
	/**
	 * initializes data from a stream (used by subclasses of a given model).
	 * @param in
	 * @throws IOException
	 * @throws ClassNotFoundException
	 */
	public void initFromFile(ObjectInputStream in) throws IOException, ClassNotFoundException;
}

[editar] org.girino.frac.operators.InverseBuddhaOperator.java

package org.girino.frac.operators;
 
 
/**
 * So, this is not inverse at all, this is actually the right one! 
 * I thought it was inverse at first because i was dumb, 
 * stupid or something.. don't really know!
 * @author girino
 *
 */
public class InverseBuddhaOperator extends NormalBuddhaOperator {
 
	private int depth_blue = 50;
	private int depth_green = 200;
	private int depth_red = 1000;
 
	/**
	 * So this is where I check if the iteration s deserving of being plotted and
	 * record the points i want plotted. I dont plot, this is the view's part.
	 * 
	 * In this, I get fast escapes in blue, medium escapes in green and
	 * slow escapes (up do getMaxIterations()) in red.
	 * I don't plot non escapes (> getMaxIterations()).
	 * 
	 */
	public void apply(int numIterations, double mag2, int[][] cacheImage) {
		if (mag2 < 4) {
			return;
		} else if ( (numIterations < getDepth_blue()) ) { 
			for (int j = 0; j < numIterations; j++)
			{
				if (cacheImage[j][0] < size && cacheImage[j][1] < size && cacheImage[j][0] >= 0 && cacheImage[j][1] >= 0) {
					blueImage[cacheImage[j][0]][cacheImage[j][1]]++;
					if (blueImage[cacheImage[j][0]][cacheImage[j][1]] > maxBlue) {
						maxBlue = blueImage[cacheImage[j][0]][cacheImage[j][1]];
					}
				}
			}
		} else if ( (numIterations < getDepth_green()) ) { 
			for (int j = 0; j < numIterations; j++)
			{
				if (cacheImage[j][0] < size && cacheImage[j][1] < size && cacheImage[j][0] >= 0 && cacheImage[j][1] >= 0) {
					greenImage[cacheImage[j][0]][cacheImage[j][1]]++;
					if (greenImage[cacheImage[j][0]][cacheImage[j][1]] > maxGreen) {
						maxGreen = greenImage[cacheImage[j][0]][cacheImage[j][1]];
					}
				}
			}
		} else if ( (numIterations < getDepth_red()) ) { 
			for (int j = 0; j < numIterations; j++)
			{
				if (cacheImage[j][0] < size && cacheImage[j][1] < size && cacheImage[j][0] >= 0 && cacheImage[j][1] >= 0) {
					redImage[cacheImage[j][0]][cacheImage[j][1]]++;
					if (redImage[cacheImage[j][0]][cacheImage[j][1]] > maxRed) {
						maxRed = redImage[cacheImage[j][0]][cacheImage[j][1]];
					}
				}
			}
		}
 
	}
 
	/**
	 * maximum number of iterations i'm interested on. 
	 * This is for the model to know when to stop.
	 */
	public int getMaxIterations() {
		return getDepth_red();
	}
	/**
	 * these are for helping the view compensating colors.
	 */
	public void setDepth_blue(int depth_blue) {
		this.depth_blue = depth_blue;
	}
	/**
	 * these are for helping the view compensating colors.
	 */
	public int getDepth_blue() {
		return depth_blue;
	}
	/**
	 * these are for helping the view compensating colors.
	 */
	public void setDepth_green(int depth_green) {
		this.depth_green = depth_green;
	}
	/**
	 * these are for helping the view compensating colors.
	 */
	public int getDepth_green() {
		return depth_green;
	}
	/**
	 * these are for helping the view compensating colors.
	 */
	public void setDepth_red(int depth_red) {
		this.depth_red = depth_red;
	}
	/**
	 * these are for helping the view compensating colors.
	 */
	public int getDepth_red() {
		return depth_red;
	}
 
}

[editar] org.girino.frac.operators.NormalBuddhaOperator.java

package org.girino.frac.operators;
 
import java.applet.Applet;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
 
import org.girino.frac.BuddhaOperator;
 
/**
 * I thought this was normal, but it's actually inverse. 
 * I'm too lazy to rename now!
 * 
 * This is also base class for all other controlers.
 * 
 * @author girino
 *
 */
public class NormalBuddhaOperator implements BuddhaOperator {
 
	private static final int MAGIC_NUMBER = 20060614;
	private int depth_blue = 500;
	private int depth_green = 2000;
	private int depth_red = 5000;
 
	/** guess I figured out subclasses might need these... dunno... */
	protected long[][] redImage;
	/** guess I figured out subclasses might need these... dunno... */
	protected long[][] greenImage;
	/** guess I figured out subclasses might need these... dunno... */
	protected long[][] blueImage;
	/** guess I figured out subclasses might need these... dunno... */
	protected long maxRed;
	/** guess I figured out subclasses might need these... dunno... */
	protected long maxGreen;
	/** guess I figured out subclasses might need these... dunno... */
	protected long maxBlue;
	/** guess I figured out subclasses might need these... dunno... */
	protected int size;
 
	/**
	 * creates new image arrays and initializes params.
	 */
	public void init(Applet parent) {
		size = Integer.parseInt(parent.getParameter("tamanho"));
		setRedImage(new long[size][size]);
		setGreenImage(new long[size][size]);
		setBlueImage(new long[size][size]);
	}
 
	/**
	 * calculations. 
	 * gets pretty much the not escaped points. Blue for those not escaping
	 * in few iterations, red for thos not escaping at all (at least in 
	 * getMaxIterations() iterations). Green for the medium ones.
	 */
	public void apply(int numIterations, double mag2, int[][] cacheImage) {
		if ( (numIterations >= getDepth_blue()) ) { 
			for (int j = 0; j < getDepth_blue(); j++)
			{
				if (cacheImage[j][0] < size && cacheImage[j][1] < size && cacheImage[j][0] >= 0 && cacheImage[j][1] >= 0) {
					blueImage[cacheImage[j][0]][cacheImage[j][1]]++;
					if (blueImage[cacheImage[j][0]][cacheImage[j][1]] > maxBlue) {
						maxBlue = blueImage[cacheImage[j][0]][cacheImage[j][1]];
					}
				}
			}
		} 
		if ( (numIterations >= getDepth_red()) ) { 
			for (int j = 0; j < getDepth_red(); j++)
			{
				if (cacheImage[j][0] < size && cacheImage[j][1] < size && cacheImage[j][0] >= 0 && cacheImage[j][1] >= 0) {
					redImage[cacheImage[j][0]][cacheImage[j][1]]++; 
					if (redImage[cacheImage[j][0]][cacheImage[j][1]] > maxRed) {
						maxRed = redImage[cacheImage[j][0]][cacheImage[j][1]];
					}
				}
			}
		} 
		if ( (numIterations >= getDepth_green()) ) { 
			for (int j = 0; j < getDepth_green(); j++)
			{
				if (cacheImage[j][0] < size && cacheImage[j][1] < size && cacheImage[j][0] >= 0 && cacheImage[j][1] >= 0) {
					greenImage[cacheImage[j][0]][cacheImage[j][1]]++;
					if (greenImage[cacheImage[j][0]][cacheImage[j][1]] > maxGreen) {
						maxGreen = greenImage[cacheImage[j][0]][cacheImage[j][1]];
					}
				}
			}
		}
 
	}
 
	/**
	 * calculates mandelbrot set for a point.
	 */
	public double calcReal(double x, double y, double x0, double y0) {
   		return x*x - y*y + x0;
	}
	/**
	 * calculates mandelbrot set for a point.
	 */
	public double calcImaginary(double x, double y, double x0, double y0) {
   		return 2*x*y + y0;
	}
 
	/**
	 * @see BuddhaOperator#getBlueImage()
	 */
	public long[][] getBlueImage() {
		return blueImage;
	}
	/**
	 * @see BuddhaOperator#setBlueImage(long[][])
	 */
	public void setBlueImage(long[][] blueImage) {
		this.blueImage = blueImage;
	}
	/**
	 * @see BuddhaOperator#getGreenImage()
	 */
	public long[][] getGreenImage() {
		return greenImage;
	}
	/**
	 * @see BuddhaOperator#setGreenImage(long[][])
	 */
	public void setGreenImage(long[][] greenImage) {
		this.greenImage = greenImage;
	}
	/**
	 * @see BuddhaOperator#getRedImage()
	 */
	public long[][] getRedImage() {
		return redImage;
	}
	/**
	 * @see BuddhaOperator#setRedImage(long[][])
	 */
	public void setRedImage(long[][] redImage) {
		this.redImage = redImage;
	}
 
	/**
	 * @see BuddhaOperator#getMaxIterations();
	 */
	public int getMaxIterations() {
		return getDepth_red();
	}
 
	/**
	 * @see BuddhaOperator#getMaxBlue()
	 */
	public long getMaxBlue() {
		return maxBlue;
	}
 
	/**
	 * @see BuddhaOperator#getMaxGreen()
	 */
	public long getMaxGreen() {
		return maxGreen;
	}
 
	/**
	 * @see BuddhaOperator#getMaxRed()
	 */
	public long getMaxRed() {
		return maxRed;
	}
 
	/**
	 * @see BuddhaOperator#dumpToFile(String)
	 */
	public void dumpToFile(String filename) throws IOException {
		System.out.println("Writing...");
		FileOutputStream fout = new FileOutputStream(filename);
		ObjectOutputStream out = new ObjectOutputStream(fout);
		// first we write some file identifier. In our case I chose todays date
		out.writeObject(new Integer(MAGIC_NUMBER));
		// then write the class name for current object. Or better,
		// write the class object so we just need to read it afterwards.
		out.writeObject(this.getClass());
		// now delegate
		internalDumpToFile(out);
		// flush and close
		out.close();
		fout.flush();
		fout.close();
		System.out.println("Writen!");
	}
 
	/**
	 * yeah, this is for subclasses. Maybe Ishould add to the
	 * interface? this stores subclass specific data.
	 */
	protected void internalDumpToFile(ObjectOutputStream out) throws IOException {
		// for most classes this might work... which means, writes down max values
		// and image arrays in the order R then G then B.
		out.writeObject(redImage);
		out.writeObject(greenImage);
		out.writeObject(blueImage);
		// max values come afterwards so we avoid sync errors ;)
		// actually we don't avoid "errors", just exceptions.
		out.writeObject(new Long(maxRed)