buddhabrot.java

/*
 *   Buddhabrot Java applet
 *
 *   This program is free software; you can redistribute it and/or
 *   modify it under the terms of the GNU General Public License
 *   as published by the Free Software Foundation; either version
 *   2 of the License, or (at your option) any later version.
 *
 *   Hans Liss <Hans@Liss.pp.se>
 *   http://hans.liss.pp.se
 *
 */
 
import java.awt.*;
import java.awt.geom.Point2D.Double;
import java.awt.image.*;
import java.awt.event.*;
import java.applet.*;
import java.io.*;
 
public class buddhabrot extends Applet implements Runnable,MouseListener,MouseMotionListener {
    private int tmpBuffer_r[];
    private int tmpBuffer_g[];
    private int tmpBuffer_b[];
    private int hist_r[], hist_g[], hist_b[];
    private int vmax_r, vmax_g, vmax_b;
    private BufferedImage ofsImage;
    private Graphics ofsG;
    private WritableRaster ofsRaster;
    private int[] ofsPixels;
    private volatile Thread paintThread = null;
 
    private int width;
    private int height;
    private int wh;
 
    private int limit_r;
    private int limit_g;
    private int limit_b;
    private int maxiter;
    private int lasthits;
 
    private double gc_x;
    private double gc_y;
    private double gd_x;
    private double gd_y;
    private double gv_x;
    private double gv_y;
 
    private int select_x0;
    private int select_y0;
    private int select_x1;
    private int select_y1;
    private int last_x1;
    private int last_y1;
 
    public static void main (String args[])
    {
        System.out.println ("Run this program in a browser or in AppletViewer");
    }
 
    private int getIntParam(String pname, int defaultValue) {
        String param = getParameter(pname);
        if (param != null) {
            try {
                int N = Integer.parseInt(param);
                if (N > 0)
                    return N;
            }
            catch (NumberFormatException e) {
            }
        }
        return defaultValue;
    }
 
    public void init()
    {
        gc_x=-0.6134;
        gc_y=0;
        gd_x=4;
        gd_y=3;
 
        select_x0=0; select_y0=0; select_x1=0; select_y1=0;
 
        width = getSize().height;
        height = getSize().width;
 
        limit_r=getIntParam("limit_r", 1000);
        limit_g=getIntParam("limit_g", 25);
        limit_b=getIntParam("limit_b", 12);
 
        maxiter=(Math.max(limit_r, Math.max(limit_g, limit_b)));
 
        int tmpw=(int)((double)height * gd_x / gd_y);
        int tmph=(int)((double)width * gd_y / gd_x);
        if (tmpw < width)
            width=tmpw;
        else if (tmph < height)
            height=tmph;
 
        gv_x=gd_x/(double)width;
        gv_y=gd_y/(double)height;
 
        wh = width * height;
 
        tmpBuffer_r = new int[wh];
        tmpBuffer_g = new int[wh];
        tmpBuffer_b = new int[wh];
        ofsImage = new BufferedImage(height, width, BufferedImage.TYPE_INT_ARGB);
        ofsG = ofsImage.getGraphics();
        ofsRaster = ofsImage.getRaster();
        ofsPixels = ( (DataBufferInt) ofsRaster.getDataBuffer()).getData();
 
        for (int i = 0 ; i < wh; i++) {
            tmpBuffer_r[i] = 0;
            tmpBuffer_g[i] = 0;
            tmpBuffer_b[i] = 0;
        }
        vmax_r=vmax_g=vmax_b = 0;
        hist_r=hist_g=hist_b=null;
        addMouseMotionListener( this );
        addMouseListener( this );
    }
 
    public void start() {
        if (paintThread == null) {
            paintThread = new Thread(this, "Paint");
            paintThread.start();
        }
    }
 
    public void mouseClicked(MouseEvent e) {
    }
 
    public void mouseEntered(MouseEvent e) {
    }
 
    public void mouseExited(MouseEvent e) {
    }
 
    public void mousePressed(MouseEvent e) {
        int x=e.getY();
        int y=e.getX();
        select_x0=x;
        select_y0=y;
        select_x1=x;
        select_y1=y;
        last_x1=x;
        last_y1=y;
        e.consume();
    }
 
    public void mouseMoved(MouseEvent e) {
    }
 
    public void mouseDragged(MouseEvent e) {
        int x=e.getY();
        int y=e.getX();
        Graphics g = getGraphics();
        if (x != select_x1 || y != select_y1) {
            int x0 = Math.min(select_x0, select_x1);
            int y0 = Math.min(select_y0, select_y1);
            int dx=Math.abs(select_x1 - select_x0);
            int dy=Math.abs(select_y1 - select_y0);
            g.setXORMode(getBackground());
            if (dx>0 || dy>0) g.drawRect(y0, x0, dy, dx);
            select_x1=x;
            select_y1=y;
            dx=Math.abs(select_x1 - select_x0);
            dy=Math.abs(select_y1 - select_y0);
            if (dx < dy * width / height) {
                dx = dy * width/height;
                if (select_x1 < select_x0) select_x1 = select_x0 - dx;
                else select_x1=select_x0 + dx;
            }
            else if (dy < dx * height / width) {
                dy = dx * height/width;
                if (select_y1 < select_y0) select_y1 = select_y0 - dy;
                else select_y1=select_y0 + dy;
            }
            x0 = Math.min(select_x0, select_x1);
            y0 = Math.min(select_y0, select_y1);
            if (dx>0 || dy>0) g.drawRect(y0, x0, dy, dx);
        }
        e.consume();
    }
 
    public void mouseReleased(MouseEvent e)
    {
        int x=e.getY();
        int y=e.getX();
        select_x1=x;
        select_y1=y;
        int dx=Math.abs(select_x1 - select_x0);
        int dy=Math.abs(select_y1 - select_y0);
        double x0 = Math.min(select_x0, select_x1);
        double y0 = Math.min(select_y0, select_y1);
        if (e.isMetaDown()) {
            gd_x *= 2;
            gd_y *= 2;
        } else if (dx == 0 && dy == 0) {
            gc_x += gd_x * ((double)(x0 - width/2) / (double)width);
            gc_y += gd_y * ((double)(y0 - height/2) / (double)height);
            gd_x /= 2;
            gd_y /= 2;
        } else {
            if (dx < dy * width / height) {
                dx = dy * width / height;
                if (select_x1 < select_x0) select_x1 = select_x0 - dx;
                else select_x1=select_x0 + dx;
            }
            else if (dy < dx * height / width) {
                dy = dx * height / width;
                if (select_y1 < select_y0) select_y1 = select_y0 - dy;
                else select_y1=select_y0 + dy;
            }
            gc_x += gd_x * ((double)(x0 + dx/2 - width/2) / (double)width);
            gc_y += gd_y * ((double)(y0 + dy/2 - height/2) / (double)height);
            gd_x *= (double)dx/(double)width;
            gd_y *= (double)dy/(double)height;
        }
        gv_x=gd_x/(double)width;
        gv_y=gd_y/(double)height;
 
        select_x0=0; select_y0=0; select_x1=0; select_y1=0;
        for (int i = 0 ; i < wh; i++) {
            tmpBuffer_r[i] = 0;
            tmpBuffer_g[i] = 0;
            tmpBuffer_b[i] = 0;
        }
        vmax_r=vmax_g=vmax_b = 0;
        hist_r=hist_g=hist_b=null;
        paintThread = new Thread(this, "Paint");
        paintThread.start();
        e.consume();
    }
 
    public void paint(Graphics g)
    {
        super.paint(g);
 
        int hival_r;
        int hival_g;
        int hival_b;
        int i, j;
 
                synchronized(this) {
                    for (i=0; i<width*height; i++) {
                        double col_r=0, col_g=0, col_b=0;
                        for (hival_r = vmax_r; hival_r > 0 && hist_r[hival_r]<=4; hival_r--);
                        for (hival_g = vmax_g; hival_g > 0 && hist_g[hival_g]<=4; hival_g--);
                        for (hival_b = vmax_b; hival_b > 0 && hist_b[hival_b]<=4; hival_b--);
                        if (hival_r > 0) col_r=(double)(tmpBuffer_r[i])/(double)hival_r;
                        if (hival_g > 0) col_g=(double)(tmpBuffer_g[i])/(double)hival_g;
                        if (hival_b > 0) col_b=(double)(tmpBuffer_b[i])/(double)hival_b;
                        if (col_r > 1) col_r=1;
                        if (col_g > 1) col_g=1;
                        if (col_b > 1) col_b=1;
 
                        ofsPixels[i] = 0xff000000 | ((int)(255*col_r) << 16) | ((int)(255*col_g) << 8) | (int)(255*col_b);
                    }
                    if (select_x1 != select_x0 && select_y1 != select_y0) {
                        int x0 = Math.min(select_x0, select_x1);
                        int x1 = Math.max(select_x0, select_x1);
                        int y0 = Math.min(select_y0, select_y1);
                        int y1 = Math.max(select_y0, select_y1);
                        int dx=x1-x0;
                        int dy=y1-y0;
                        ofsG.drawRect(y0, x0, dy, dx);
                    }
                }
        g.drawImage( ofsImage, 0, 0, this);
    }
 
    int mandel(double cx, double cy) {
        int i=maxiter, xpos, ypos;
        int hits=0;
        double
            zx=cx,
            zy=cy,
            y,
            zx2=cx*cx,
            zy2=cy*cy;
 
        while((i > 0) && (zx2 + zy2 < 4.0)) {
            zy=2*zx*zy + cy;
            zx=zx2-zy2 + cx;
            zx2=zx*zx;
            zy2=zy*zy;
            i--;
        }
        if (i>0) {
            int n=i;
            i=maxiter;
            zx=cx;
            zy=cy;
            zx2=cx*cx;
            zy2=cy*cy;
            while((i > 0) && (zx2 + zy2 < 4.0)) {
                zy=2*zx*zy + cy;
                zx=zx2-zy2 + cx;
                zx2=zx*zx;
                zy2=zy*zy;
                i--;
                xpos=(int)((zx - (gc_x - gd_x/2))/gv_x);
                ypos=(int)((zy - (gc_y - gd_y/2))/gv_x);
                if (xpos >= 0 && xpos < width && ypos >= 1 && ypos < height) {
                    hits++;
                    int arpos=ypos + height * xpos;
                    if (n > maxiter - limit_r) {
                        tmpBuffer_r[arpos]++;
                        if (tmpBuffer_r[arpos] > vmax_r) {
                            int hist2[]=new int[tmpBuffer_r[arpos]+1];
                            if (vmax_r > 0)
                                System.arraycopy(hist_r, 0, hist2, 0, vmax_r+1);
                            hist_r=hist2;
                            for (int j=vmax_r+1; j<=tmpBuffer_r[arpos]; j++) hist_r[j]=0;
                            vmax_r=tmpBuffer_r[arpos];
                        }
                        hist_r[tmpBuffer_r[arpos]]++;
                    }
                    if (n > maxiter - limit_g) {
                        tmpBuffer_g[arpos]++;
                        if (tmpBuffer_g[arpos] > vmax_g) {
                            int hist2[]=new int[tmpBuffer_g[arpos]+1];
                            if (vmax_g > 0)
                                System.arraycopy(hist_g, 0, hist2, 0, vmax_g+1);
                            hist_g=hist2;
                            for (int j=vmax_g+1; j<=tmpBuffer_g[arpos]; j++) hist_g[j]=0;
                            vmax_g=tmpBuffer_g[arpos];
                        }
                        hist_g[tmpBuffer_g[arpos]]++;
                    }
                    if (n > maxiter - limit_b) {
                        tmpBuffer_b[arpos]++;
                        if (tmpBuffer_b[arpos] > vmax_b) {
                            int hist2[]=new int[tmpBuffer_b[arpos]+1];
                            if (vmax_b > 0)
                                System.arraycopy(hist_b, 0, hist2, 0, vmax_b+1);
                            hist_b=hist2;
                            for (int j=vmax_b+1; j<=tmpBuffer_b[arpos]; j++) hist_b[j]=0;
                            vmax_b=tmpBuffer_b[arpos];
                        }
                        hist_b[tmpBuffer_b[arpos]]++;
                    }
                }
            }
        }
        lasthits=hits;
        return i;
    }
 
    public void mutate(Double m) {
        if (Math.random() > 0.95) {
            m.setLocation(Math.random()*4-2, Math.random()*4-2);
        } else {
            double phi=Math.random() * Math.PI * 2;
            double r=(gd_x / 100) * Math.exp(Math.random() * 4);
            double mx=m.getX() + r*Math.cos(phi);
            double my=m.getY() + r*Math.sin(phi);
            m.setLocation(mx, my);
        }
    }
 
    public double TransitionProbability(int n1, int n2) {
        return (1 - ((double)n1/maxiter)) / (1 - ((double)n2/maxiter));
    }
 
    public void run() {
        Thread myThread = Thread.currentThread();
        int oldhits=-1;
        long n=10000000000L;
        int oldt=0;
 
        double cx=0;
        double cy=0;
        while (true) {
            if (paintThread != myThread) return;
            synchronized(this) {
                if (oldhits == -1) {
                    oldt=mandel(cx, cy);
                    oldhits=lasthits;
                }
                Double m=new Double(cx,cy);
                mutate(m);
                int newt=mandel(m.getX(), m.getY());
                int newhits=lasthits;
                double t1=TransitionProbability(newt, oldt);
                double t2=TransitionProbability(oldt, newt);
                if (oldhits > 0) {
                    double alpha=Math.min(1, Math.exp(Math.log((double)newhits * t1) - Math.log((double)oldhits * t2)));
                    if (alpha > Math.random()) {
                        cx = m.getX();
                        cy = m.getY();
                        oldt=newt;
                        oldhits=newhits;
                    }
                } else {
                    cx = m.getX();
                    cy = m.getY();
                    oldt=newt;
                    oldhits=newhits;
                }
                if ((newhits > 0) && (((n--) % 20) == 0)) {
                    if (n == 0) n=10000000000L;
                    repaint();
                }
            }
        }
    }
 
    public void stop() {
        paintThread = null;
    }
 
    public void update ( Graphics g ){
        paint(g);
    }
}