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(); } }