import javax.swing.*;
import java.awt.*;
import java.awt.image.*;
import java.util.*;
import java.awt.event.*;
import javax.swing.event.*;
import java.text.*;
import java.lang.Math.* ;
class FractalGrid extends JPanel implements ActionListener, MouseMotionListener, MouseListener, KeyListener, ChangeListener {
private double x1,y1,x2,y2;
private double c_re=1.0,c_im=1.0; //for julia fractal
private int rep,cx,cy;
private int palette[];
//private BufferedImage img;
private Image img;
private MemoryImageSource mis;
private int buf[];
private int width,height;
private int markx1,marky1,markx2,marky2,mark,resmark;
private final double X1=-2.8,Y1=-2.,X2=1.8,Y2=2.;
//components
private JButton b_repp,b_repm,b_def,b_pal;
private JRadioButton rb_m,rb_j,rb_bs;
private JSpinner s_cre,s_cim; //for julia c_re/c_im values
private JLabel l_cre,l_cim;
private JColorChooser cc_pal; //for palette
private final int Mandel=0;
private final int Julia=1;
private final int BS=2;
private int fractaltype=Mandel;
private void defaults() {
x1=X1;
y1=Y1;
x2=X2;
y2=Y2;
}
public void update() {
if (rep>1000) rep=1000;
else
if (rep<10) rep=10;
switch (fractaltype) {
case Mandel: Mandelbrot(); break;
case Julia: Julia(); break;
case BS: BS(); break;
}
mis.newPixels();
}
private int RGB(int r,int g,int b)
{
return 0xff000000+((r&255)<<16)+((g&255)<<8)+(b&255);
}
private void setpalette(int r,int g,int b)
{
int mr=1,ar=0;
int mg=1,ag=0;
int mb=1,ab=0;
if (r<128) {mr=-1; ar=255; r=255-r;}
if (g<128) {mg=-1; ag=255; g=255-g;}
if (b<128) {mb=-1; ab=255; b=255-b;}
for (int i=0; i<256; i++) palette[255-i]=RGB((ar+mr*i)*r/255,(ag+mg*i)*g/255,(ab+mb*i)*b/255);
}
public FractalGrid(int w,int h,int _rep) {
rep=_rep;
width=w; height=h;
mark=0;
//img=new BufferedImage(w,h,BufferedImage.TYPE_INT_RGB);
buf=new int[w*h];
mis=new MemoryImageSource(w,h,buf,0,w); //provides fast buffered drawing
mis.setAnimated(true);
img=createImage(mis);
defaults();
setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
setLayout(null);
//BUTTONS
b_repp=new JButton("Inc rep.");
b_repm=new JButton("Dec rep.");
b_def=new JButton("Defaults");
b_pal=new JButton("Set col.");
b_repp.addActionListener(this);
b_repm.addActionListener(this);
b_def.addActionListener(this);
b_pal.addActionListener(this);
b_repp.setActionCommand("rep++");
b_repm.setActionCommand("rep--");
b_def.setActionCommand("def");
b_pal.setActionCommand("pal");
b_repp.setToolTipText("Increases number of steps to generate fractal");
b_repm.setToolTipText("Decreases number of steps to generate fractal");
b_pal.setToolTipText("Choose fractal palette color");
add(b_repp);
add(b_repm);
add(b_def);
add(b_pal);
//Insets insets=getInsets();
Dimension size=b_repp.getPreferredSize();
size.height/=2;
w=2;
h=height-size.height-2;
b_repp.setBounds(w,h,size.width,size.height);
w+=size.width;
size=b_repm.getPreferredSize();
size.height/=2;
h=height-size.height-2;
b_repm.setBounds(w,h,size.width,size.height);
w+=size.width;
size=b_def.getPreferredSize();
size.height/=2;
h=height-size.height-2;
b_def.setBounds(w,h,size.width,size.height);
w+=size.width;
size=b_pal.getPreferredSize();
size.height/=2;
h=height-size.height-2;
b_pal.setBounds(w,h,size.width,size.height);
w+=size.width;
b_repp.setFocusable(false);
b_repm.setFocusable(false);
b_def.setFocusable(false);
b_pal.setFocusable(false);
//RADIO BUTTONS
JRadioButton rb_m=new JRadioButton("Mandelbrot");
JRadioButton rb_j=new JRadioButton("Julia");
JRadioButton rb_bs=new JRadioButton("Burning Ship");
ButtonGroup group=new ButtonGroup();
group.add(rb_m);
group.add(rb_j);
group.add(rb_bs);
rb_m.addActionListener(this);
rb_j.addActionListener(this);
rb_bs.addActionListener(this);
rb_m.setSelected(true);
rb_m.setActionCommand("mandel");
rb_j.setActionCommand("julia");
rb_bs.setActionCommand("bs");
add(rb_m);
add(rb_j);
add(rb_bs);
rb_m.setFocusable(false);
rb_j.setFocusable(false);
rb_bs.setFocusable(false);
size=rb_bs.getPreferredSize();
w=width-size.width-2;
size.height/=2;
h=height-size.height-2;
rb_bs.setBounds(w,h,size.width,size.height);
rb_bs.setBackground(new Color(255,255,255,255));
size=rb_j.getPreferredSize();
w-=size.width;
size.height/=2;
h=height-size.height-2;
rb_j.setBounds(w,h,size.width,size.height);
rb_j.setBackground(new Color(255,255,255,255));
size=rb_m.getPreferredSize();
w-=size.width;
size.height/=2;
h=height-size.height-2;
rb_m.setBounds(w,h,size.width,size.height);
rb_m.setBackground(new Color(255,255,255,255));
//SPINNER
//c_re
SpinnerModel smodel=new SpinnerNumberModel(c_re,-10.0,10.0,0.1);
l_cre=new JLabel("C_RE");
s_cre=new JSpinner(smodel);
s_cre.addChangeListener(this);
h-=size.height;
size=s_cre.getPreferredSize();
w=2;
h-=size.height;
s_cre.setBounds(w,h,size.width,size.height);
l_cre.setBounds(w,h-size.height,size.width,size.height);
l_cre.setVisible(false);
s_cre.setVisible(false);
add(l_cre);
add(s_cre);
s_cre.setFocusable(false);
l_cre.setFocusable(false);
//c_im
smodel=new SpinnerNumberModel(c_im,-10.0,10.0,0.1);
l_cim=new JLabel("C_IM");
s_cim=new JSpinner(smodel);
s_cim.addChangeListener(this);
w+=size.width+2;
size=s_cim.getPreferredSize();
s_cim.setBounds(w,h,size.width,size.height);
l_cim.setBounds(w,h-size.height,size.width,size.height);
l_cim.setVisible(false);
s_cim.setVisible(false);
add(l_cim);
add(s_cim);
s_cim.setFocusable(false);
l_cim.setFocusable(false);
//PALETTE
cc_pal=new JColorChooser();
add(cc_pal);
cc_pal.setVisible(false);
size=cc_pal.getPreferredSize();
cc_pal.setBounds(20,20,size.width,size.height);
cc_pal.setFocusable(false);
palette=new int[256];
setpalette(255,255,255);
}
public void SetRep(int r) {rep=r;}
public void SetCurPos(int x,int y) {
cx=x;
cy=y;
}
public double mandelbrot(double x,double y,int rep) {
double z_re=0,z_im=0;
int i=-1;
while ((i<rep)&&(z_re*z_re+z_im*z_im<4))
{
double pom=z_re;
z_re=z_re*z_re-z_im*z_im+x;
z_im=2*pom*z_im+y;
i++;
}
return (double)i/(double)rep;
}
public double julia(double x,double y,double c_re,double c_im,int rep) {
double z_re=x,z_im=y;
int i=0;
while ((i<rep)&&(z_re*z_re+z_im*z_im<4))
{
double pom=z_re;
z_re=z_re*z_re-z_im*z_im+c_re;
z_im=2*pom*z_im+c_im;
i++;
}
return (double)i/(double)rep;
}
public double bs(double x,double y,int rep) {
double z_re=0,z_im=0;
int i=-1;
while ((i<rep)&&(z_re*z_re+z_im*z_im<4))
{
double re=Math.abs(z_re),im=Math.abs(z_im);
z_re=re*re-im*im+x;
z_im=2*re*im+y;
i++;
}
return (double)i/(double)rep;
}
public void Mandelbrot() {
double dx=(x2-x1)/width;
double dy=(y2-y1)/height;
double y=y1;
int l=0;
for (int i=0; i<height; i++)
{
double x=x1;
for (int j=0; j<width; j++)
{
int c=(int)(mandelbrot(x,y,rep)*255.);
//img.setRGB(j,i,palette[c]);
buf[l++]=palette[c];
x+=dx;
}
y+=dy;
}
}
public void Julia() {
double dx=(x2-x1)/width;
double dy=(y2-y1)/height;
double y=y1;
int l=0;
for (int i=0; i<height; i++)
{
double x=x1;
for (int j=0; j<width; j++)
{
int c=(int)(julia(x,y,c_re,c_im,rep)*255.);
//img.setRGB(j,i,palette[c]);
buf[l++]=palette[c];
x+=dx;
}
y+=dy;
}
}
public void BS() {
double dx=(x2-x1)/width;
double dy=(y2-y1)/height;
double y=y1;
int l=0;
for (int i=0; i<height; i++)
{
double x=x1;
for (int j=0; j<width; j++)
{
int c=(int)(bs(x,y,rep)*255.);
//img.setRGB(j,i,palette[c]);
buf[l++]=palette[c];
x+=dx;
}
y+=dy;
}
}
void scale(double sx,double sy)
{
x1*=sx;
x2*=sx;
y1*=sy;
y2*=sy;
}
double getX(int x) {return (double)x/(double)width*(x2-x1)+x1;}
double getY(int y) {return (double)y/(double)height*(y2-y1)+y1;}
public void paintComponent(Graphics g) {
g.drawImage(img,0,0,null,null);
//for double number formation
NumberFormat nf=NumberFormat.getInstance();
nf.setMaximumFractionDigits(4);
nf.setMinimumFractionDigits(4);
g.setColor(new Color(palette[255]));
if (mark!=0) {
g.drawLine(markx1,marky1,markx2,marky1);
g.drawLine(markx2,marky1,markx2,marky2);
g.drawLine(markx1,marky2,markx2,marky2);
g.drawLine(markx1,marky1,markx1,marky2);
Font font=new Font(Font.DIALOG,Font.PLAIN,10);
g.setFont(font);
g.drawString("x1 = "+nf.format(getX(markx1)),5,40);
g.drawString("y1 = "+nf.format(getY(marky1)),5,50);
g.drawString("x2 = "+nf.format(getX(markx2)),5,60);
g.drawString("y2 = "+nf.format(getY(marky2)),5,70);
}
/*else {
g.drawLine(cx,0,cx,height);
g.drawLine(0,cy,width,cy);
}*/
Font font=new Font(Font.DIALOG,Font.PLAIN,10);
g.setFont(font);
g.drawString("x = "+nf.format(getX(cx)),5,10);
g.drawString("y = "+nf.format(getY(cy)),5,20);
g.drawString("rep = "+rep,5,30);
}
//from ActionListener
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand()=="rep++")
{
rep+=10;
update();
}
else
if (e.getActionCommand()=="rep--")
{
rep-=10;
update();
}
else
if (e.getActionCommand()=="def")
{
defaults();
update();
}
else
if (e.getActionCommand()=="pal")
{
Color col=cc_pal.showDialog(this,"Choose color",new Color(palette[0]));
if (col!=null)
{
setpalette(col.getRed(),col.getGreen(),col.getBlue());
update();
}
}
else
if (e.getActionCommand()=="mandel")
{
fractaltype=Mandel;
l_cre.setVisible(false);
s_cre.setVisible(false);
l_cim.setVisible(false);
s_cim.setVisible(false);
update();
}
else
if (e.getActionCommand()=="julia")
{
fractaltype=Julia;
l_cre.setVisible(true);
s_cre.setVisible(true);
l_cim.setVisible(true);
s_cim.setVisible(true);
update();
}
else
if (e.getActionCommand()=="bs")
{
fractaltype=BS;
l_cre.setVisible(false);
s_cre.setVisible(false);
l_cim.setVisible(false);
s_cim.setVisible(false);
update();
}
repaint();
}
//from MouseMotionListener
public void mouseDragged(MouseEvent e) {
if (mark!=0) {
markx2=e.getX();
marky2=e.getY();
if (resmark==1)
{
marky2=marky1+(int)((double)(markx2-markx1)*(double)((double)height/(double)width));
}
SetCurPos(e.getX(),e.getY());
repaint();
}
}
public void mouseMoved(MouseEvent e) {
//System.out.println(e.getX()+" "+e.getY());
SetCurPos(e.getX(),e.getY());
repaint();
}
//from MouseListener
public void mouseClicked(MouseEvent e) {
}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
public void mousePressed(MouseEvent e) {
if (mark==0)
{
markx1=e.getX();
marky1=e.getY();
}
mark=e.getButton()==e.BUTTON1 ? 1:-1;
resmark=0;
}
public void mouseReleased(MouseEvent e) {
markx2=e.getX();
marky2=e.getY();
if (mark==1)
{
if (resmark==1)
{
marky2=marky1+(int)((double)(markx2-markx1)*(double)((double)height/(double)width));
}
if ((Math.abs(markx2-markx1)>1)&&(Math.abs(marky2-marky1)>1))
{
double px1=getX(markx1);
double py1=getY(marky1);
double px2=getX(markx2);
double py2=getY(marky2);
x1=px1; x2=px2; y1=py1; y2=py2;
update();
repaint();
}
}
else
if (mark==-1)
{
if (resmark==1)
{
marky2=marky1+(int)((double)(markx2-markx1)*(double)((double)height/(double)width));
}
if ((Math.abs(markx2-markx1)>1)&&(Math.abs(marky2-marky1)>1))
{
double dx=(x2-x1)/(double)(markx2-markx1);
double dy=(y2-y1)/(double)(marky2-marky1);
x1=getX(markx1)-(double)markx1*dx;
y1=getY(marky1)-(double)marky1*dy;
x2=x1+dx*(double)width;
y2=y1+dy*(double)height;
update();
repaint();
}
}
mark=0;
}
private void keyfunc(KeyEvent e) {
int key=e.getKeyCode();
double nx=0.1*(x2-x1),ny=0.1*(y2-y1);
switch (key)
{
case KeyEvent.VK_EQUALS:
rep+=10;
update();
break;
case KeyEvent.VK_MINUS:
rep-=10;
update();
break;
case KeyEvent.VK_HOME:
defaults();
update();
break;
case KeyEvent.VK_ESCAPE:
mark=0;
break;
case KeyEvent.VK_CONTROL:
resmark=1;
break;
case KeyEvent.VK_UP:
y1-=ny;
y2-=ny;
update();
break;
case KeyEvent.VK_DOWN:
y1+=ny;
y2+=ny;
update();
break;
case KeyEvent.VK_LEFT:
x1-=nx;
x2-=nx;
update();
break;
case KeyEvent.VK_RIGHT:
x1+=nx;
x2+=nx;
update();
break;
}
repaint();
}
//from KeyListener
public void keyPressed(KeyEvent e) {
keyfunc(e);
}
public void keyReleased(KeyEvent e) {
if (e.getKeyCode()==KeyEvent.VK_CONTROL) resmark=0;
}
public void keyTyped(KeyEvent e) {}
//from ChangeListener
public void stateChanged(ChangeEvent e) {
c_re=((SpinnerNumberModel)s_cre.getModel()).getNumber().doubleValue();
c_im=((SpinnerNumberModel)s_cim.getModel()).getNumber().doubleValue();
update();
repaint();
}
}