randel.java

/*
 *   Auto-colouring, randmly sampled 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 randel extends Applet implements Runnable,MouseListener,MouseMotionListener {
    private int sumBuffer[];
    private int countBuffer[];
    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 lasthits;
    private boolean mouseInside=false;
 
    private int MAXITER=65535;
 
    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 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");
    }
 
    public void init()
    {
        gc_x=-0.6134;
        gc_y=0;
        gd_x=3.136;
        gd_y=2.352;
 
        vmax=0;
        hist=new int[1];
 
        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;
 
        gv_x=gd_x/(double)width;
        gv_y=gd_y/(double)height;
 
        sumBuffer = new int[wh];
        countBuffer = 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 i = 0 ; i < wh; i++) {
            sumBuffer[i] = 0;
            countBuffer[i] = 0;
        }
        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) {
        mouseInside=true;
    }
 
    public void mouseExited(MouseEvent e) {
        mouseInside=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) {
    }
 
    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);
        Thread oldPaintThread = paintThread;
        paintThread = new Thread(this, "Paint");
        while (oldPaintThread.isAlive());
        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;
        }
        gv_x=gd_x/(double)width;
        gv_y=gd_y/(double)height;
        select_x0=0; select_y0=0; select_x1=0; select_y1=0;
        vmax=0;
        hist=new int[1];
        for (int i = 0 ; i < wh; i++) {
            sumBuffer[i] = 0;
            countBuffer[i] = 0;
        }
        paintThread.start();
        e.consume();
    }
 
    public void paint(Graphics g)
    {
        super.paint(g);
 
        int loval, hival, i, j;
        for (hival=vmax; hival>0 && hist[hival]<width*height/2000; hival--);
        for (loval=1; loval<=hival && hist[loval]<width*height/2000; loval++);
        for (i=0; i<wh; i++) {
            if (countBuffer[i] == 0 || sumBuffer[i]/countBuffer[i] == MAXITER) {
                col_r=0;
                col_g=0;
                col_b=0;
            } else {
                double val=sumBuffer[i]/countBuffer[i];
                double h=0;
                if (hival > loval)
                    h=(double)((val-loval) % (MAXITER-loval))/(double)(MAXITER-loval);
 
                double s=h;
                double v=h;
                h *= 255.2;
                h += 0.7;
                double v1 = Math.floor(h); h -= v1;
                s *= 211.7;
                v1 = Math.floor(s); s -= v1;
                s = 1-s;
                v *= 111.7;
                v1 = Math.floor(v); v -= v1;
                v = 1-v;
                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);
        }
        if (mouseInside) {
            ofsG.setColor(Color.white);
            ofsG.fillRect(2,2,width-4,36);
            ofsG.setColor(Color.black);
            ofsG.setFont(new Font("Helvetica", Font.PLAIN, 9));
            String info="[vmax="+vmax+",hival="+hival+",loval="+loval+"]";
            ofsG.drawString(info, width-150, 32);
        }
        ofsG.setColor(Color.black);
        ofsG.setFont(new Font("Helvetica", Font.PLAIN, 9));
        String info1="[(x,y)=("+gc_x+","+gc_y+")";
        String info2="dx="+gd_x+"]";
        ofsG.drawString(info1, 20, 16);
        ofsG.drawString(info2, 20, 32);
        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--;
        }
 
        int rx=(int)((cx - (gc_x - gd_x/2))/gv_x);
        int ry=(int)((cy - (gc_y - gd_y/2))/gv_y);
        int pos=((height-1-ry)*width+rx)%wh;
        sumBuffer[pos] += (MAXITER - i);
        countBuffer[pos]++;
        int newval=sumBuffer[pos]/countBuffer[pos];
        if (newval > vmax) {
            int hist2[]=new int[newval + 1];
            if (vmax > 0)
                System.arraycopy(hist, 0, hist2, 0, vmax+1);
            hist=hist2;
            for (int j=vmax+1; j<=newval; j++) hist[j]=0;
            vmax=newval;
        } else hist[newval]++;
 
        return 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 mutate(Double m) {
        int x, y;
        double mx, my;
        if (Math.random() > 0.95) {
            do {
                mx=(double)(Math.random()*width)*gv_x + (gc_x - gd_x/2);
                my=(double)(Math.random()*height)*gv_y + (gc_y - gd_y/2);
                x=(int)((mx - (gc_x - gd_x/2))/gv_x);
                y=(int)((my - (gc_y - gd_y/2))/gv_y);
            } while (x < 0 || x >= width || y < 0 || y >= height);
            m.setLocation(mx, my);
        } else {
            do {
                double phi=Math.random() * Math.PI * 2;
                double r=(gd_x / 100) * Math.exp(Math.random() * 4);
                mx=m.getX() + r*Math.cos(phi);
                my=m.getY() + r*Math.sin(phi);
                x=(int)((mx - (gc_x - gd_x/2))/gv_x);
                y=(int)((my - (gc_y - gd_y/2))/gv_y);
            } while (x < 0 || x >= width || y < 0 || y >= height);
            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();
        for (int i=0; i<=vmax; i++) hist[i]=0;
        long n=10000000000L;
        int oldhits=-1;
        int oldt=0;
 
        double cx=(double)(Math.random()*width)*gv_x + (gc_x - gd_x/2);
        double cy=(double)(Math.random()*height)*gv_y + (gc_y - gd_y/2);
 
        while (true) {
            if (paintThread != myThread) return;
 
            if (oldhits == -1) {
                oldt=mandel(cx, cy);
                if (oldt>0) oldhits=1; else oldhits=0;
            }
 
            Double m=new Double(cx,cy);
            mutate(m);
            int newt=mandel(m.getX(), m.getY());
            int newhits;
            if (newt>0) newhits=1; else newhits=0;
            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 (((n--) % 100) == 0)
                repaint();
        }
    }
 
    public void stop() {
        paintThread = null;
    }
 
    public void update ( Graphics g ){
        paint(g);
    }
}