/* The Mandelbrot Set Copyright (c) 1996 Simon Arthur For more information about this program, contact chroma@mindspring.com. */ import java.awt.*; import java.applet.*; public class MandelTest extends Applet implements Runnable { MandelControls controls; MandelCanvas c; // TimeThread T; public void init() { String s; // parameter string float pa,pb,pc,pd; // temp vars for parameters s = getParameter("xstart"); pa = (s==null) ? -2 : Float.valueOf(s).floatValue(); s = getParameter("ystart"); pb = (s==null) ? -2 : Float.valueOf(s).floatValue(); s = getParameter("xend"); pc = (s==null) ? 2 : Float.valueOf(s).floatValue(); s = getParameter("yend"); pd = (s==null) ? 2 : Float.valueOf(s).floatValue(); MandelCanvas c = new MandelCanvas(pa,pb,pc,pd); BorderLayout l = new BorderLayout(); setLayout(l); add("Center",c); c.resize(100,100); controls = new MandelControls(c); add("South",controls); controls.resize(200,200); c.controls = controls; layout(); c.repaint(); // start new thread so that the other one will multitask properly // T=new TimeThread(); // T.start(); // T.setPriority(Thread.MIN_PRIORITY); } public void start() { controls.enable(); } public void run() { controls.enable(); } public void stop() { controls.disable(); } } /***************************************** MandelCanvas ******************************************/ class MandelCanvas extends Canvas { boolean filled = false; Font font; float Xmin,Ymin,Xmax,Ymax; Thread mythread=null; boolean DrawMandel=true; boolean DrawMarqueeMode; boolean DrawThread=false; // Keep track of whether the drawing // thread has been created MandelArtist mymandel=null; Marquee mymarquee=new Marquee(); String artistName = "artthread"; Rectangle r; MandelControls controls; public MandelCanvas (float a, float b, float c, float d) { Xmin=a; Ymin=b; Xmax=c; Ymax=d; } public void paint(Graphics g) { r=bounds(); mymarquee.boundx = r.width; mymarquee.boundy = r.height; if (DrawMandel) { if (!DrawThread) { // we know that the Drawing thread hasn't been made //first give mymandel the information it needs to draw mymandel = new MandelArtist(g,artistName); //We'll lower the priority a little so it will pre-empt properly mymandel.setPriority(Thread.MIN_PRIORITY); mymandel.maxx = r.width; mymandel.maxy = r.height; mymandel.xstart = Xmin; mymandel.ystart = Ymin; mymandel.xend = Xmax; mymandel.yend = Ymax; //System.out.println(Xmin);System.out.println(Xmax);System.out.println(Ymin);System.out.println(Ymax); mymandel.needsRepaint=true; // now we'll start a new thread of mymandel so it draws in the background mymandel.start(); DrawThread=true; } else { mymandel.needsRepaint=true; } } mymarquee.draw(g); } public void redraw(float Xmin, float Ymin,float Xmax, float Ymax) { mymandel.stop(); // make a new thread the next time we need to repaint DrawThread=false; DrawMandel=true; this.Xmin=Xmin; this.Ymin=Ymin; this.Xmax=Xmax; this.Ymax=Ymax; repaint(); } public boolean mouseUp(Event evt, int x, int y) { mymarquee.resize(x,y); controls.f3.setText(String.valueOf(Xmin + (x * ((Xmax-Xmin)/r.width) ) ) ); controls.f4.setText(String.valueOf(Ymin + (y * ((Ymax-Ymin)/r.height) ) ) ); repaint(); return true; } public boolean mouseDown(Event evt, int x, int y) { mymarquee.reset(); // stop the MandelArtist drawing thread mymandel.stop(); // make a new thread the next time we need to repaint DrawThread=false; // stop drawing the Mandelbrot for now DrawMandel=false; // Set new anchor point for marquee mymarquee.setAnchorPoint(x,y); controls.f1.setText(String.valueOf(Xmin + (x * ((Xmax-Xmin)/r.width) ) ) ); controls.f2.setText(String.valueOf(Ymin + (y * ((Ymax-Ymin)/r.height) ) ) ); mymarquee.show(); return true; } public boolean mouseDrag(Event evt, int x, int y) { mymarquee.resize(x,y); controls.f3.setText(String.valueOf(Xmin + (x * ((Xmax-Xmin)/r.width) ) ) ); controls.f4.setText(String.valueOf(Ymin + (y * ((Ymax-Ymin)/r.height) ) ) ); repaint(); return true; } public void update(Graphics g) { mymarquee.draw(g); if (DrawMandel) { paint(g); } } } /***************************************** MandelControls ******************************************/ class MandelControls extends Panel { TextField f1, f2, f3, f4; Label l1,l2,l3,l4,ltest; Button b1,b2,b3,b4,breset; Choice ch; Dimension d; MandelCanvas canvas; // set up our buttons, edit fields, etc. public MandelControls(MandelCanvas canvas) { this.canvas = canvas; GridLayout l=new GridLayout(0,4); setLayout(l); this.setBackground(Color.gray); this.setForeground(Color.blue); // add("z0", new Label("")); add("z1", new Label("")); // add("z2", new Label("")); add("z3", new Label("")); add("l1",l1 = new Label(" Xmin="+canvas.Xmin)); add("l2",l2 = new Label(" Ymin="+canvas.Ymin)); add("l3",l3 = new Label(" Xmax="+canvas.Xmax)); add("l4",l4 = new Label(" Ymax="+canvas.Ymax)); add("f1",f1 = new TextField(String.valueOf(canvas.Xmin),4)); add("f2",f2 = new TextField(String.valueOf(canvas.Ymin),4)); add("f3",f3 = new TextField(String.valueOf(canvas.Xmax),4)); add("f4",f4 = new TextField(String.valueOf(canvas.Ymax),4)); add("b1",b1 = new Button("Zoom In")); add("b2",b2 = new Button("Zoom Out")); add("b3",b3 = new Button("Julia Set")); b3.disable(); //don't have this working yet add("b4",b4 = new Button("Redraw")); add("breset",breset = new Button("Reset Values")); ch = new Choice(); ch.addItem("Small"); ch.addItem("Medium"); ch.addItem("Big"); ch.select("Big"); add("ch1",ch); } public void setNewValues() { //update text fields with info about // new Mandelbrot set l1.setText(" Xmin="+canvas.Xmin); l2.setText(" Ymin="+canvas.Ymin); l3.setText(" Xmax="+canvas.Xmax); l4.setText(" Ymax="+canvas.Ymax); f1.setText(String.valueOf(canvas.Xmin)); f2.setText(String.valueOf(canvas.Ymin)); f3.setText(String.valueOf(canvas.Xmax)); f4.setText(String.valueOf(canvas.Ymax)); } public boolean action(Event ev, Object arg) { if (ev.target.equals(b1)) { //zooom in -- min and max fix it if the user has // switched Xmin & Xmax or Ymin and Ymax canvas.redraw(Math.min(Float.valueOf(f1.getText().trim()).floatValue(),Float.valueOf(f3.getText().trim()).floatValue()) , Math.min(Float.valueOf(f2.getText().trim()).floatValue(),Float.valueOf(f4.getText().trim()).floatValue()) , Math.max(Float.valueOf(f1.getText().trim()).floatValue(),Float.valueOf(f3.getText().trim()).floatValue()) , Math.max(Float.valueOf(f2.getText().trim()).floatValue(),Float.valueOf(f4.getText().trim()).floatValue()) ); } else if (ev.target.equals(b2)) { // zoom out canvas.redraw(canvas.Xmin-(canvas.Xmax-canvas.Xmin)/2, canvas.Ymin-(canvas.Ymax-canvas.Ymin)/2, canvas.Xmax+(canvas.Xmax-canvas.Xmin)/2, canvas.Ymax+(canvas.Ymax-canvas.Ymin)/2); } else if (ev.target.equals(b3)) { // Julia set /* canvas.redraw(canvas.Xmin, canvas.Ymin, canvas.Xmax, canvas.Ymax); */ } else if (ev.target.equals(b4)) { // redraw canvas.redraw(canvas.Xmin, canvas.Ymin, canvas.Xmax, canvas.Ymax); } else if (ev.target.equals(breset)) { // reset values canvas.redraw(-2,-2,2,2); } else if (ev.target.equals(ch)) { // change size of picture if (ch.getSelectedItem()=="Small") { canvas.mymarquee.hide(); canvas.resize(50,50); canvas.redraw(canvas.Xmin,canvas.Ymin,canvas.Xmax,canvas.Ymax); canvas.move(1,1); } else if (ch.getSelectedItem()=="Medium") { canvas.mymarquee.hide(); canvas.resize(100,100); canvas.redraw(canvas.Xmin,canvas.Ymin,canvas.Xmax,canvas.Ymax); canvas.move(1,1); } else if (ch.getSelectedItem()=="Big") { canvas.mymarquee.hide(); canvas.resize(300,300); canvas.redraw(canvas.Xmin,canvas.Ymin,canvas.Xmax,canvas.Ymax); canvas.move(1,1); } } setNewValues(); return true; } } /***************************************** MandelArtist ******************************************/ class MandelArtist extends Thread { boolean needsRepaint = false; Graphics gcontext; int maxx, maxy; // these are the upper limits of the pixels we will be // plotting to float xstart, ystart, xend, yend; // these are the coordinates // of the area of the Mandelbrot set that we will be drawing public MandelArtist (Graphics g, String s) { // System.out.println(" - MandelArtist.constructor"); // copy the graphics context for use in this object gcontext=g.create(); setName(s); // set the new thread's name // gcontext.drawString("constructor", 10, 80); } public void run() { gcontext.drawString("run", 10, 10); // System.out.println(" - MandelArtist.run"); while (true) { try { sleep(500); } catch (Exception E) { stop(); } if (needsRepaint) { doDraw(); } } } // public void stop() { // needsRepaint=false; // } private int iterate (double x, double y) { // long aold, zsquared=0, xi=(long)(x*10000), yi=(long)(y*10000), ai=0, bi=0; /* Iterate over the complex value with real component x * and imaginary component y */ int iter=0,maxiter=500; double aold, bold, a2old=Double.MAX_VALUE, b2old=Double.MAX_VALUE, zsquared,a=0,b=0,asquared=0,bsquared=0; aold=0; bold=0; asquared=a*a; a=x; bsquared=b*b; b=y; zsquared=asquared+bsquared; for (iter=0; iter4 ) break; bold=b; aold=a; } /* // an attempt at using integer math to do calculations for (ai=0,bi=0,iter=0; iter<500; iter++) { // calculate value for each point aold=ai; ai=ai*ai-bi*bi+xi; bi=2*aold*bi+yi; zsquared=ai*ai+bi*bi; if ( zsquared>35919520) break; }*/ // System.out.println(zsquared); return iter; } public void doDraw() { /* Draw the mandelbrot into graphics context gcontext maxx and maxy are the max numbers of pixels xstart, ystart, xend and yend marks off the area of the Mandelbrot set that we will draw. */ int iter,selcolor,plotx, ploty=0, iterold, plotyold; float oldx=xstart, oldy=ystart; float zsquared, xlen=xend-xstart, ylen=yend-ystart; float xincr=xlen/maxx, yincr=ylen/maxy; Color c[]=new Color[255]; String s; //System.out.println(" - MandelArtist.doDraw"); needsRepaint=false; // we won't need to repaint unless we get interrupted // while drawing in which case whis will be set to // false by some other method. for(iter=0;iter<255; iter++) { c[iter]=new Color(iter*26,iter*2,iter*35); } for (double x = xstart ; x < xend ; x+=xincr) { plotx = (int)( (x-xstart) * (maxx/xlen) ); for (double y = ystart ; y < yend ; y+=yincr) { // Loop over each point on M-set iter=0; iter=iterate(x,y); gcontext.setColor(c[iter%255]); ploty=(int)( (y-ystart) * (maxy/ylen) ); gcontext.drawLine( plotx , ploty , plotx , ploty ); } } } } /************************************************************************* Marquee *************************************************************************/ class Marquee { // stuff for drawing our Marquee (selection rectangle) int rectAnchorX=0, rectAnchorY=0, rectTermX=0, rectTermY=0; // keep track of where we've last put the marquee on the screen int x1, y1, x2, y2; int boundx=Integer.MAX_VALUE, boundy=Integer.MAX_VALUE; //don't make it bigger than these values boolean hidden=true, needs_hide=false; public void draw(Graphics g) { // draw the Marquee, if we need to if (!hidden) { if (x1!=rectAnchorX || y1!=rectAnchorY || rectTermY!=y2 ||rectTermX!=x2 ) { // we need to erase the old rectangle put_rectangle(g, x1,y1,x2,y2); //now draw new one put_rectangle(g,rectAnchorX, rectAnchorY, rectTermX, rectTermY); x1=rectAnchorX; y1=rectAnchorY; x2=rectTermX; y2=rectTermY; hidden=false; } } else { // hidden //if it has not already been erased, erase it, otherwise do nothing if (needs_hide) { put_rectangle(g,rectAnchorX, rectAnchorY, rectTermX, rectTermY); } } } public boolean inMarquee(int x, int y) { //find out if the specified point is inside the marquee // System.out.println("Marquee.inmarquee"); // System.out.println(x); // System.out.println(rectAnchorX); // System.out.println(rectTermX); // System.out.println(rectAnchorY); // System.out.println(rectTermY); if ( ( (x>rectAnchorX && xrectTermX && xrectAnchorY && yrectTermY && y