mandel.java

/*
 *   Mandelbrot graph 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.image.*;
import java.awt.event.*;
import java.applet.*;
import java.io.*;
 
public class mandel extends Applet implements Runnable,MouseListener,MouseMotionListener {
    private int tmpBuffer[];
    int vmax;
    private int hist[];
    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 maxpos;
 
    private int MAXITER=65535;
    private double angle;
    private double speed;
 
    private int col_r;
    private int col_g;
    private int col_b;
 
    private double gc_x;
    private double gc_y;
    private double gd_x;
    private double gd_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;
 
    private boolean doOrbit=false;
    private int mouse_x, mouse_y;
 
    public static void main (String args[])
    {
        System.out.println ("Run this program in a browser or in AppletViewer");
    }
 
    public void init()
    {
        gc_x=-0.6134;
        gc_y=0;
        gd_x=3.136;
        gd_y=2.352;
 
        maxpos=0;
 
        vmax=500;
        hist=new int[501];
 
        select_x0=0; select_y0=0; select_x1=0; select_y1=0;
 
        width = getSize().width;
        height = getSize().height;
 
        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;
 
        wh = width * height;
 
        tmpBuffer = new int[wh];
        ofsImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
        ofsG = ofsImage.getGraphics();
        ofsRaster = ofsImage.getRaster();
        ofsPixels = ( (DataBufferInt) ofsRaster.getDataBuffer()).getData();
 
        for (int y = 0 ; y < height; y++) {
            for (int x = 0 ; x < width; x++) {
                ofsPixels[x + y*width] = 0xFF000000;
                tmpBuffer[x + y*width] = -1;
            }
        }
        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) {
        mouse_x=e.getX();
        mouse_y=e.getY();
        doOrbit=true;
    }
 
    public void mouseExited(MouseEvent e) {
        doOrbit=false;
    }
 
    public void mousePressed(MouseEvent e) {
        int x=e.getX();
        int y=e.getY();
        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) {
        mouse_x=e.getX();
        mouse_y=e.getY();
    }
 
    public void mouseDragged(MouseEvent e) {
        int x=e.getX();
        int y=e.getY();
        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(x0, y0, dx, dy);
            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(x0, y0, dx, dy);
        }
        e.consume();
    }
 
    public void mouseReleased(MouseEvent e)
    {
        int x=e.getX();
        int y=e.getY();
        select_x1=x;
        select_y1=y;
        int dx=Math.abs(select_x1 - select_x0);
        int dy=Math.abs(select_y1 - select_y0);
        if (e.isMetaDown()) {
            gd_x *= 2;
            gd_y *= 2;
        } else if (dx == 0 && dy == 0) {
            gc_x += gd_x * ((double)(select_x0 - width/2) / (double)width);
            gc_y += gd_y * ((double)((height-1-select_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;
            }
            double x0 = Math.min(select_x0, select_x1);
            double y0 = Math.max(select_y0, select_y1);
            gc_x += gd_x * ((double)(x0 + dx/2 - width/2) / (double)width);
            gc_y += gd_y * ((double)((height-1-y0) + dy/2 - height/2) / (double)height);
            gd_x *= (double)dx/(double)width;
            gd_y *= (double)dy/(double)height;
        }
        select_x0=0; select_y0=0; select_x1=0; select_y1=0;
        for (int i = 0 ; i < wh; i++) {
            tmpBuffer[i] = -1;
        }
        paintThread = new Thread(this, "Paint");
        paintThread.start();
        e.consume();
    }
 
    public void paint(Graphics g)
    {
        super.paint(g);
 
        int cpos=maxpos;
        int loval, hival, i, j;
        for (hival=vmax; hival>=0 && hist[hival]<20; hival--);
        for (loval=0; loval<=hival && hist[loval]==0; loval++);
        for (i=0; i<=cpos; i++) {
            if (tmpBuffer[i] != -1) {
                if (tmpBuffer[i]==MAXITER) {
                    col_r=0;
                    col_g=0;
                    col_b=0;
                } else {
                    double h=0;
                    if (hival > loval)
                        h=(double)((tmpBuffer[i]-loval) % (hival-loval))/(double)(hival-loval);
 
                    double s=h;
                    h *= 1.2;
                    h += 0.7;
                    double v1 = Math.floor(h); h -= v1;
                    s *= 1.7;
                    v1 = Math.floor(s); s -= v1;
                    s = 1-s;
                    double v = 1;
                    hsv2rgb(h,s,v);
                }
                ofsPixels[i] = 0xff000000 | (col_r << 16) | (col_g << 8) | 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(x0, y0, dx, dy);
        }
        String info1="[(x,y)=("+gc_x+","+gc_y+")";
        String info2="dx="+gd_x+"]";
        ofsG.setColor(Color.black);
        ofsG.setFont(new Font("Helvetica", Font.PLAIN, 12));
        ofsG.drawString(info1, 20, 20);
        ofsG.drawString(info2, 20, 42);
        if (doOrbit) plotOrbit(ofsG, mouse_x, mouse_y);
        g.drawImage( ofsImage, 0, 0, this);
    }
 
    int mandel(double cx, double cy) {
        int i=MAXITER;
        double
            zx=cx,
            zy=cy,
            y,
            zx2=cx*cx,
            zy2=cy*cy,
            ox=cx,
            oy=cy,
            ccx=cx,
            ccy=cy;
        while((i > 0) && (zx2 + zy2 < 4.0)) {
            ox=zx;
            oy=zy;
            zy=2*zx*zy + ccy;
            zx=zx2-zy2 + ccx;
            zx2=zx*zx;
            zy2=zy*zy;
            i--;
        }
        speed=Math.sqrt((zx-ox)*(zx-ox)+(zy-oy)*(zy-oy));
        angle=Math.atan2((zx-ox),(zy-oy))+3.141592;
        return i;
    }
 
    void plotOrbit(Graphics g, int x, int y) {
        int i=16;
        double cx=gd_x * ((double)x/(double)width) + gc_x - (gd_x/2);
        double cy=gd_y * ((double)(height - 1 - y)/(double)height) + gc_y - (gd_y/2);
        int ox=x;
        int oy=y;
        double
            zx=cx,
            zy=cy,
            zx2=cx*cx,
            zy2=cy*cy,
            ccx=cx,
            ccy=cy;
        while((i > 0) && (zx2 + zy2 < 4.0)) {
            zy=2*zx*zy + ccy;
            zx=zx2-zy2 + ccx;
            zx2=zx*zx;
            zy2=zy*zy;
            int nx=(int)((zx - (gc_x - (gd_x/2))) * (double)width / gd_x);
            int ny=height - 1 - (int)((zy - (gc_y - (gd_y/2))) * (double)height / gd_y);
            g.setColor(Color.red);
            g.drawLine(ox, oy, nx, ny);
            ox=nx;
            oy=ny;
            i--;
        }
    }
 
    public void hsv2rgb(double H,double S,double V) {
        int i;
        double f,p,q,t;
        H = H*6;
        if (H>=6)
            H-=6;
        i=(int)H;
        f=H-(double)i;
        p=V*(1.0-S);
        q=V*(1.0-(S*f));
        t=V*(1.0-(S*(1.0-f)));
 
        switch(i) {
        case 0: col_r=(int)(V*255); col_g=(int)(t*255); col_b=(int)(p*255); break;
        case 1: col_r=(int)(q*255); col_g=(int)(V*255); col_b=(int)(p*255); break;
        case 2: col_r=(int)(p*255); col_g=(int)(V*255); col_b=(int)(t*255); break;
        case 3: col_r=(int)(p*255); col_g=(int)(q*255); col_b=(int)(V*255); break;
        case 4: col_r=(int)(t*255); col_g=(int)(p*255); col_b=(int)(V*255); break;
        case 5: col_r=(int)(V*255); col_g=(int)(p*255); col_b=(int)(q*255); break;
        }
    }
 
    public void run() {
        Thread myThread = Thread.currentThread();
        for (int i=0; i<=vmax; i++) hist[i]=0;
        for (int y=height-1; y >= 0; y--) {
            for (int x=0; x < width; x++) {
                if (paintThread != myThread) return;
                double cx=gd_x * ((double)x/(double)width) + gc_x - (gd_x/2);
                double cy=gd_y * ((double)y/(double)height) + gc_y - (gd_y/2);
                double h, s, v;
                int intgr;
                int val=mandel(cx, cy);
                maxpos=(height-1-y)*width+x;
                tmpBuffer[maxpos] = MAXITER - val;
                if (val != 0) {
                    if ((MAXITER-val) > vmax) {
                        vmax=MAXITER-val;
                        hist=new int[vmax+1];
                        for (int i=0; i<=maxpos; i++) {
                            if (tmpBuffer[i] >=0 && tmpBuffer[i] != MAXITER)
                                hist[tmpBuffer[i]]++;
                        }
                    } else hist[MAXITER-val]++;
                }
            }
            repaint();
        }
        while (true) {
            try {myThread.sleep(20);}
            catch(InterruptedException e) {}
            if (doOrbit) repaint();
        }
    }
 
    public void stop() {
        paintThread = null;
    }
 
    public void update ( Graphics g ){
        paint(g);
    }
}