ec2-18-191-44-23.us-east-2.compute.amazonaws.com | ToothyWiki | MandelbrotSet | RecentChanges | Login | Webcomic
This is in two classes because I wanted multi-threading for the Mandelbrot/Julia set interaction. You can see two instances of the applet working side-by-side at [1] (or, if it's down, which it seems to be half the time nowadays, on the5k.org, in the 2002 section. This, incidentally, is the reason for the naming of the applets - I wanted the names to take up as little space as possible, although I wasn't quite willing to call my fields f, ff, fff, ffff, etc).import java.applet.*;
import java.awt.*;
import java.awt.event.*;
public class F extends Applet
implements MouseListener,
MouseMotionListener
{
// Some useful constants
final static int screenSize=400;
final static int s2=screenSize/2;
double midX=0.0;
double midY=0.5;
double range=0.5;
final static int LIMIT=250;
int mouseFunction;
Color[] lup;
// Cursors
Cursor crosshair = Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR);
Cursor hand = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);
Cursor arrow = Cursor.getDefaultCursor();
// Other mouse stuff
int mousex, mousey;et = false;
// Is this a Mandelbrot instantiation or a Julia instantiation?
boolean mbrot;
// A Julia instantiation needs the coefficient - default give pretty set
double jreal = 0.07;
double jimag = 0.6275;
// The two complex numbers used for the computation and one temp
double zr;
double newzr;
double zi;
double cr;
double ci;
// Two images for buffering: the first for the fractal, the second for
// drawing the magnifying glass without flicker
Image image;
Image bufimage;
// A thread to do the drawing
Thread ff;
public void init()
{
// I want mouse capability
this.addMouseListener(this);
this.addMouseMotionListener(this);
// Check whether this is a Mandelbrot or Julia instantiation.
mbrot = !(getParameter("t").equals("J"));
// Initialise the mouse function and the pointer
if (mbrot)
{
mouseFunction = 0;
setCursor(crosshair);
}
else
{
mouseFunction = 1;
setCursor(hand);
}
// Set up the colour lookup table
lup = new Color[LIMIT + 2];
for (int i = 0; i < LIMIT; i++)
{
lup[i] = Color.getHSBColor((float)(i%64)/64.0f,
(float)(0.6 + 0.4* Math.cos((double)i/40.0)),
1.0f);
}
lup[LIMIT] = Color.black;
lup[LIMIT + 1] = Color.black;
// Initialise the image
image = createImage(400, mbrot ? 440 : 460);
bufimage = createImage(400, mbrot ? 440 : 460);
plot();
// Start drawing
plot();
}
// Provide the MouseListener interface
public void mousePressed(MouseEvent e)
{
// If the mouse was clicked in an area with y-coord less than 400, that's in the
// picture. Otherwise it's a button at the bottom.
int x = e.getX();
int y = e.getY();
// calculate creal and cimag
// I use screenSize-y because Im increases up the screen
double creal = midX - range + (double)x/s2*range;
double cimag = midY - range + (double)(screenSize-y)/s2*range;
// round to precise figures as poss
double rounding = screenSize/range;
creal = Math.rint(rounding*creal)/rounding;
cimag = Math.rint(rounding*cimag)/rounding;
if (y < 400)
{
switch (mouseFunction)
{
case 0:
// If this is a Mandelbrot, call the Julia applet
if (mbrot)
{
((F)getAppletContext().getApplet("J")).j(creal, cimag);
}
break;
case 1:
// Recenter on the clicked area
midX = creal;
midY = cimag;
plot();
break;
case 2:
// Zoom in
midX = creal;
midY = cimag;
range /= 2;
plot();
break;
case 3:
// Zoom out
range *= 2;
plot();
break;
default:
// do nothing;
break;
}
}
else if (y < 440)
{
// Function = x / 100
if (mbrot || x > 99)
{
mouseFunction = x / 100;
drawButtons();
switch (mouseFunction)
{
case 0:
setCursor(crosshair);
break;
case 1:
setCursor(hand);
break;
case 2:
case 3:
setCursor(arrow);
}
}
}
}
// I don't want to use most of the mouse stuff yet
public void mouseReleased(MouseEvent e) {}
public void mouseClicked(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e)
{
mouseInApplet = false;
paint(this.getGraphics());
}
// Method by which new co-ords can be passed to a Julia instance
public void j(double creal, double cimag)
{
jreal = creal;
jimag = cimag;
plot();
}
// Paint method
public void paint(Graphics g)
{
if (mouseInApplet && getCursor() == arrow)
{
// Draw the magnifying glass
Graphics g2 = bufimage.getGraphics();
g2.setColor(Color.black);
g2.drawImage(image, 0, 0, null);
g2.drawOval(mousex + 12, mousey - 1, 8, 8);
g2.drawLine(mousex + 18, mousey + 5, mousex + 22, mousey + 11);
g2.drawLine(mousex + 21, mousey, mousex + 25, mousey);
if (mouseFunction == 2)
{
g2.drawLine(mousex + 23, mousey - 2, mousex + 23, mousey + 2);
}
g.drawImage(bufimage, 0, 0, null);
}
else
{
g.drawImage(image, 0, 0, null);
}
}
void drawButtons()
{
// Get Graphics object from image
Graphics g = image.getGraphics();
// Add the buttons at the bottom
g.setColor(Color.cyan);
g.fillRect(0, 400, 400, 60);
g.setColor(Color.blue);
g.fillRect(100 * mouseFunction, 400, 100, 40);
// Text on the buttons
g.setColor(Color.black);
g.setFont(new Font("Monospaced",Font.PLAIN, 14));
if (mbrot)
{
g.drawString("Julia",25,425);
}
else
{
StringBuffer parameter = new StringBuffer();
parameter.append("Parameter: ");
if (jreal != 0.0)
parameter.append(jreal);
if (jimag > 0)
parameter.append("+" + jimag + "i");
else if (jimag < 0)
parameter.append(jimag + "i");
else if (jreal == 0)
parameter.append("0");
g.drawString(parameter.toString(),
10,
455);
}
g.drawString("Centre", 125, 425);
g.drawString("Zoom in", 225, 425);
g.drawString("Zoom out", 325, 425);
// Paint applet
paint(this.getGraphics());
}
// MouseMotionListener events
public void mouseMoved(MouseEvent evt)
{
updateMousePosition(evt);
}
public void mouseDragged(MouseEvent evt)
{
}
public void updateMousePosition(MouseEvent evt)
{
mousex = evt.getX();
mousey = evt.getY();
mouseInApplet = true;
paint(this.getGraphics());
}
// Destroy any existing thread, and create a new one
void plot()
{
if (ff != null)
{
ff.stop();
}
ff = new Ff(this);
ff.start();
}
}
Second class: the thread which does the painting.
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
public class Ff extends Thread
{
F f;
final static int screenSize=400;
final static int s2=screenSize/2;
final static int LIMIT=250;
// The two complex numbers used for the computation and one temp
double zr;
double newzr;
double zi;
double cr;
double ci;
double midX;
double midY;
double range;
// A Julia instantiation needs the coefficient
double jreal;
double jimag;
boolean mbrot;
Ff(F f)
{
this.f = f;
this.midX = f.midX;
this.midY = f.midY;
this.range = f.range;
this.jreal = f.jreal;
this.jimag = f.jimag;
this.mbrot = f.mbrot;
}
public void run()
{
// Used for saving calls to g.setColor
int oldn = -1;
// Get Graphics object from image
Graphics g = f.image.getGraphics();
// First redraw the buttons
f.drawButtons();
// Draw onto image
for (int y = screenSize; y > 0; y--)
{
for (int x = 0; x < screenSize; x++)
{
// For a Mandelbrot applet, we want
// z = 0
// c = (range*(x-s2))/s2+midX + (range*(y-s2))/s2+midY*i
//
// For a Julia applet, we want
// z = (range*(x-s2))/s2+midX + (range*(y-s2))/s2+midY*i
// c = jreal + jimag*i
if (mbrot)
{
zr = 0;
zi = 0;
cr = (range*(x-s2))/s2+midX;
ci = (range*(y-s2))/s2+midY;
}
else
{
zr = (range*(x-s2))/s2+midX;
zi = (range*(y-s2))/s2+midY;
cr = jreal;
ci = jimag;
}
// Test to see whether the point is in the set
int n = 0;
while (n++ <LIMIT && (zr*zr + zi*zi) < 4.0)
{
// z = z*z + c;
newzr = zr*zr - zi*zi +cr;
zi = 2*zr*zi + ci;
zr = newzr;
}
// Set a colour appropriate to the divergence time
// Now uses lookup table
if (n != oldn)
g.setColor(f.lup[n]);
oldn = n;
// Draw a rectangle in this colour at the appropriate position
g.fillRect(x,screenSize-y,1,1);
}
// Paint the image with this line drawn in
f.paint(f.getGraphics());
}
}
}