amultimand.java

/*
 *   Multimand Java applet
 *   Mandelbrot graph colouring algorithm based on More Than Usual
 *   information
 *
 *   See http://hans.liss.pp.se/multimand.html for more info on this
 *
 *   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 amultimand extends Applet implements Runnable,MouseListener,MouseMotionListener {
    private int tmpBuffer_speed[];
    private int tmpBuffer_angle[];
    private int tmpBuffer_iter[];
    private int vmax_speed, vmax_angle, vmax_iter;
    private int hival_speed, hival_angle, hival_iter;
    private int hist_speed[], hist_angle[], hist_iter[];
    private Image ofsImage;
    private Graphics ofsG;
    private volatile Thread paintThread = null;
 
    private int width;
    private int height;
    private int wh;
    private int maxpos;
 
    private int maxiter=65535;
 
    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 col_r, col_g, col_b;
 
    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 final int SLEEPTIME=20;
 
    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().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;
 
        gv_x=gd_x/(double)width;
        gv_y=gd_y/(double)height;
 
        wh = width * height;
 
        tmpBuffer_speed = new int[wh];
        tmpBuffer_angle = new int[wh];
        tmpBuffer_iter = new int[wh];
        ofsImage = createImage (width, height);
        ofsG = ofsImage.getGraphics();
 
        for (int y = 0 ; y < height; y++) {
            for (int x = 0 ; x < width; x++) {
                tmpBuffer_speed[x + y*width] = 0;
                tmpBuffer_angle[x + y*width] = 0;
                tmpBuffer_iter[x + y*width] = 0;
            }
        }
        vmax_speed=vmax_angle=vmax_iter = 0;
        hival_speed=hival_angle=hival_iter = 0;
        hist_speed=new int[vmax_speed+1];
        hist_angle=new int[vmax_angle+1];
        hist_iter=new int[vmax_iter+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) {
    }
 
    public void mouseExited(MouseEvent e) {
    }
 
    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);
        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;
        for (int i = 0 ; i < wh; i++) {
            tmpBuffer_speed[i] = 0;
            tmpBuffer_angle[i] = 0;
            tmpBuffer_iter[i] = 0;
        }
        vmax_speed=vmax_angle=vmax_iter = 0;
        hival_speed=hival_angle=hival_iter = 0;
        hist_speed=new int[vmax_speed+1];
        hist_angle=new int[vmax_angle+1];
        hist_iter=new int[vmax_iter+1];
        paintThread = new Thread(this, "Paint");
        paintThread.start();
        e.consume();
    }
 
    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 paint(Graphics g)
    {
        super.paint(g);
 
        //      int hival_speed;
        //      int hival_angle;
        //      int hival_iter;
        int loval_iter;
        int i, j;
 
        double h, s, v;
        synchronized(this) {
            //      System.out.print("Painting...");
            //      for (hival_speed = vmax_speed; hival_speed > 0 && hist_speed[hival_speed]<=1; hival_speed--);
            //      for (hival_angle = vmax_angle; hival_angle > 0 && hist_angle[hival_angle]<=1; hival_angle--);
            //      for (hival_iter = vmax_iter; hival_iter > 0 && hist_iter[hival_iter]<=1; hival_iter--);
            for (loval_iter = 0; loval_iter < vmax_iter && hist_iter[hival_iter]<=1; loval_iter++);
            for (i=0; i<maxpos; i++) {
                h=s=v=0;
                if (hival_speed > 0) v=(double)(2*tmpBuffer_speed[i])/(double)hival_speed;
                if (v>2) v=2;
                if (v>1) v=2-v;
                v=0.5+v/2;
                if (hival_angle > 0) h=(double)tmpBuffer_angle[i]/(double)hival_angle;
                if (h>1) h=1;
                if (tmpBuffer_iter[i] != maxiter) {
                    if ((hival_iter-loval_iter) > 0) s=(double)(2*(tmpBuffer_iter[i]-loval_iter))/(double)(hival_iter-loval_iter);
                    else s=0;
                } else
                    s=2;
                if (s>2) s=2;
                if (s>1) s=2-s;
                s=0.5+s/2;
                hsv2rgb(h,s,v);
 
                ofsG.setColor(new Color(col_r, col_g, col_b));
                ofsG.drawLine((int)(i % width), i / width, (int)(i % width), i / width);
            }
            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);
            }
            //      System.out.println("done.");
        }
        g.drawImage( ofsImage, 0, 0, this);
    }
 
    void mandel(double cx, double cy, int arpos) {
        int i=maxiter, speed, angle;
        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--;
        }
        double v1=Math.sqrt((zx-ox)*(zx-ox)+(zy-oy)*(zy-oy)) / 100;
        if (v1 > 1) v1=1;
        speed = (int)(v1*10000);
        angle = (int)(360*(Math.atan2((zx-ox),(zy-oy))+3.141592)/6.283184);
        synchronized(this) {
            tmpBuffer_speed[arpos] = speed;
            if (tmpBuffer_speed[arpos] > vmax_speed) {
                //              System.out.println("vmax_speed changed from "+vmax_speed+" to "+tmpBuffer_speed[arpos]);
                int hist2[]=new int[tmpBuffer_speed[arpos]+1];
                if (vmax_speed > 0)
                    System.arraycopy(hist_speed, 0, hist2, 0, vmax_speed+1);
                hist_speed=hist2;
                for (int j=vmax_speed+1; j<=tmpBuffer_speed[arpos]; j++) hist_speed[j]=0;
                vmax_speed=tmpBuffer_speed[arpos];
            }
            hist_speed[tmpBuffer_speed[arpos]]++;
            if (hist_speed[tmpBuffer_speed[arpos]] > 1 && tmpBuffer_speed[arpos]>hival_speed) hival_speed=tmpBuffer_speed[arpos];
 
            tmpBuffer_angle[arpos] = angle;
            if (tmpBuffer_angle[arpos] > vmax_angle) {
                //              System.out.println("vmax_angle changed from "+vmax_angle+" to "+tmpBuffer_angle[arpos]);
                int hist2[]=new int[tmpBuffer_angle[arpos]+1];
                if (vmax_angle > 0)
                    System.arraycopy(hist_angle, 0, hist2, 0, vmax_angle+1);
                hist_angle=hist2;
                for (int j=vmax_angle+1; j<=tmpBuffer_angle[arpos]; j++) hist_angle[j]=0;
                vmax_angle=tmpBuffer_angle[arpos];
            }
            hist_angle[tmpBuffer_angle[arpos]]++;
            if (hist_angle[tmpBuffer_angle[arpos]] > 1 && tmpBuffer_angle[arpos]>hival_angle) hival_angle=tmpBuffer_angle[arpos];
 
            tmpBuffer_iter[arpos] = maxiter - i;
            if (i != 0) {
                if (tmpBuffer_iter[arpos] > vmax_iter) {
                    //              System.out.println("vmax_iter changed from "+vmax_iter+" to "+tmpBuffer_iter[arpos]);
                    int hist2[]=new int[tmpBuffer_iter[arpos]+1];
                    if (vmax_iter > 0)
                        System.arraycopy(hist_iter, 0, hist2, 0, vmax_iter+1);
                    hist_iter=hist2;
                    for (int j=vmax_iter+1; j<=tmpBuffer_iter[arpos]; j++) hist_iter[j]=0;
                    vmax_iter=tmpBuffer_iter[arpos];
                }
                hist_iter[tmpBuffer_iter[arpos]]++;
                if (hist_iter[tmpBuffer_iter[arpos]] > 1 && tmpBuffer_iter[arpos]>hival_iter) hival_iter=tmpBuffer_iter[arpos];
            }
        }
    }
 
    public void run() {
        Thread myThread = Thread.currentThread();
        int n=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);
                int arpos=x + width * (height - 1 - y);
                mandel(cx, cy, arpos);
                maxpos=arpos;
            }
            if (n++ == 20) {
                n=0;
                repaint();
                try {myThread.sleep(SLEEPTIME);}
                catch(InterruptedException e) {}
            }
        }
        repaint();
    }
 
    public void stop() {
        paintThread = null;
    }
 
    public void update ( Graphics g ){
        paint(g);
    }
}