The julia is just the standard julia animator you usually found by iterations. The way to draw the ring is not actually not mine. I found a java based mandelbrot renderer the used the algorithm to make what the author called "mandelbrot rings". Since the mandel and julia sets are rendered almost in the same way (iterating complex numbers), I just applied the "ring algo to the julia animator.
*Trying to find a link to the mandelbrot ring page. Will post when I get to find it again.
In simple terms, the rings are done in almost the same way you do 8 bit copper pipes. Less saturared colors on the edges but saturated on the middle. Also works like a lightmap but applied in a heightmap using the julia set as values that determine the RGB triplet to use for a particular pixel.
EDIT: The original site is apparently not online but it turned out that I have it on my HD so here's the whole of the tute:
Mandelbrot Rings
By Stephen C. Ferguson, January 29, 2000
This article was first posted on January 27 and has been revised on January 29.
To view the original January 27 article, click here.
This algorithm demonstrates a method for producing rings from the Mandelbrot set. The rings are produce by an orbit trap method. The orbit trap is testing if the current orbit location is within the radius of the orbit starting point. The radius limit in this case is fixed to a constant value of 0.01
void calculate ()
{
for (col=0; col<width; col+=1)
{
zr = 0;
zi = 0;
cr = x + col * hFactor;
ci = y + row * vFactor;
ztot = 0;
cMagnitude = Math.sqrt(cr*cr+i*ci);
dRadius_HI = cMagnitude+dRadius;
dRadius_LO = cMagnitude-dRadius;
zsqr1 = 4;
for (i=1; i<maxit; i+=1)
{
tzr = zr*zr - zi*zi + cr;
tzi = 2*zr*zi + ci;
zr = tzr;
zi = tzi;
zMagnitude = Math.sqrt(zr*zr+zi*zi);
if (zMagnitude < dRadius_HI && zMagnitude > dRadius_LO && i > 1)
{
ztot = Math.sqrt(ztot) + (1 - Math.abs(zMagnitude - cMagnitude) / dRadius);
}
if (zMagnitude > 2.0)
break;
}
if (ztot > 0)
i = (int) (Math.sqrt(ztot)*500);
else
i = 0;
coloring();
}
}
Here's the whole code for the mandelbrot applet:
///////////////////////////////////////////////////////////
//
// Developed by Stephen C. Ferguson
//
// First created on 3-31-99
// Last modified 1-29-2000
//
// http://home.hiwaay.net/~stephenf
//
///////////////////////////////////////////////////////////
import java.applet.Applet;
import java.awt.*;
import java.awt.image.MemoryImageSource;
import java.awt.event.*;
import java.lang.Math;
public class MandelApp
extends Applet
implements MouseListener,
MouseMotionListener,
ActionListener,
Runnable
{
Image image;
MemoryImageSource source;
int row, col;
double zr, zi, cr, ci, tzr, tzi;
double hFactor, vFactor;
double x, y;
double zsqr0;
double zsqr1;
double zsqrX;
double zsqrY;
double ztot, xtot, ytot, dRadius_HI, dRadius_LO;
double dRadius, zMagnitude, cMagnitude;
int width, height;
int maxit;
int i, j, index, i_last;
int pixels[];
int the_size;
int value;
int code, color;
int red, grn, blu;
int tmp;
MandelRect mandelRect;
Button startButton;
Button zoomButton;
boolean startCalculate;
Thread myThread;
final double zerotol = 1e-12;
public void init()
{
index = 0;
row = 0;
width = 300;
height = 300;
startCalculate = false;
pixels = new int[width*height];
mandelRect = new MandelRect (width, height);
the_size = width * height;
code = 0xFF000000;
// initialize pixel array
for (i=0; i<width*height; i+=1)
pixels[i] = 0xffffffff;
source = new MemoryImageSource(width, height, pixels, 0, width);
image = createImage(source);
setLayout(new BorderLayout());
startButton = new Button ("Start over");
zoomButton = new Button ("Zoom in");
startButton.addActionListener(this);
zoomButton.addActionListener(this);
addMouseListener(this);
addMouseMotionListener(this);
Panel p = new Panel ();
p.setLayout (new FlowLayout ());
p.add (startButton);
p.add (zoomButton);
add ("South", p);
}
public void start()
{
index = 0;
row = 0;
col = 0;
//maxit = 768;
maxit = 100;
dRadius = 0.01;
x = mandelRect.mandelX;
y = mandelRect.mandelY;
width = mandelRect.imgWidth;
height = mandelRect.imgHeight;
hFactor = mandelRect.mandelWidth/width;
vFactor = mandelRect.mandelHeight/height;
startCalculate = true;
if(myThread == null)
{
myThread = new Thread(this);
myThread.start();
}
}
public void paint(Graphics g)
{
image.flush();
g.drawImage(image, 0, 0, null);
}
public void update(Graphics g)
{
paint(g);
mandelRect.paint (g);
}
void thread_manager()
{
repaint();
try
{
myThread.sleep(250);
}
catch( InterruptedException e )
{
return;
}
}
public void run()
{
myThread = Thread.currentThread();
myThread.setPriority(Thread.MIN_PRIORITY);
while (true)
{
// Modify the values in the pixels array at (x, y, w, h)
if (startCalculate == true)
{
calculate();
row += 1;
if ((row & 0x1f) == 0)
{
repaint();
thread_manager();
}
if (index >= width*height)
{
startCalculate = false;
index = 0;
}
}
else
{
thread_manager();
}
}
}
void calculate ()
{
for (col=0; col<width; col+=1)
{
zr = 0;
zi = 0;
cr = x + col * hFactor;
ci = y + row * vFactor;
xtot = 0;
ytot = 0;
ztot = 0;
i_last = 0;
cMagnitude = Math.sqrt(cr*cr+ci*ci);
//cMagnitude = Math.abs(Math.sin(Math.abs(cr))*Math.sin(Math.abs(ci)));
//cMagnitude = Math.abs(Math.sin(40*Math.PI*cr)*Math.sin(40*Math.PI*ci));
//cMagnitude = Math.abs(Math.sin(4*Math.PI*(cr+zr))+Math.sin(4*Math.PI*(ci+zi)));
//cMagnitude = Math.abs(cr*cr/ci*ci);
//cMagnitude = Math.abs(Math.sin(cr*cr)/Math.sin(ci*ci));
//cMagnitude = Math.abs(Math.sin(cr*cr+ci*ci)/Math.cos(cr*cr+ci*ci));
//cMagnitude = Math.abs(Math.sin(cr)/Math.cos(ci));
//cMagnitude = Math.abs(Math.sin(2*Math.PI*cr)/Math.sin(2*Math.PI*ci));
dRadius_HI = cMagnitude+dRadius;
dRadius_LO = cMagnitude-dRadius;
zsqr1 = 4;
for (i=1; i<maxit; i+=1)
{
tzr = zr*zr - zi*zi + cr;
tzi = 2*zr*zi + ci;
zr = tzr;
zi = tzi;
zMagnitude = Math.sqrt(zr*zr+zi*zi);
if (zMagnitude < dRadius_HI && zMagnitude > dRadius_LO && i > 1)
{
ztot = Math.sqrt(ztot) + (1 - Math.abs(zMagnitude - cMagnitude) / dRadius);
i_last = i;
}
if (zMagnitude > 2.0)
break;
}
if (ztot > 0)
i = (int) (Math.sqrt(ztot)*500);
else
i = 0;
coloring();
}
}
void coloring()
{
if (i < 256)
red = i;
else
red = 255;
if (i < 512 && i > 255)
{
grn = i - 256;
}
else
{
if (i >= 512)
grn = 255;
else
grn = 0;
}
if (i <= 768 && i > 511)
blu = i - 512;
else
{
if (i >= 768)
blu = 255;
else
blu = 0;
}
tmp = (int) ((red+grn+blu)*0.333);
if (tmp > 0xFF)
tmp = 0xFF;
red = (int) ((red+grn+tmp)*0.33);
grn = (int) ((grn+blu+tmp)*0.33);
blu = (int) ((blu+red+tmp)*0.33);
switch (i_last % 3)
{
case 1:
// try swapping colors around
tmp = red;
red = grn;
grn = blu;
blu = tmp;
break;
case 2:
// try swapping colors around
tmp = red;
blu = grn;
red = blu;
grn = tmp;
break;
}
red = red & 0xFF;
grn = grn & 0xFF;
blu = blu & 0xFF;
red = (red << 16) & 0x00ff0000; // shift left 16 places
grn = (grn << 8) & 0x0000ff00; // shift left 8 places
color = red | grn | blu;
pixels[index] = code | color;
index+=1;
}
//////////////////////////////////////////////////
// mouse handlers to create the zoom rectangle
public void mouseClicked(MouseEvent e){}
public void mouseEntered(MouseEvent e){}
public void mouseExited(MouseEvent e){}
public void mouseReleased(MouseEvent e)
{
int x =e.getX();
int y =e.getY();
mandelRect.setWidthHeight (x, y);
mandelRect.setPaintRect (true);
repaint ();
}
public void mouseMoved(MouseEvent e){}
public void mousePressed(MouseEvent e)
{
int x =e.getX();
int y =e.getY();
mandelRect.setXY (x, y);
}
public void mouseDragged(MouseEvent e)
{
int x =e.getX();
int y =e.getY();
mandelRect.setWidthHeight (x, y);
mandelRect.setPaintRect (true);
repaint ();
}
////////////////////////////////////////////////////
// Zoom and restart button handlers
public void actionPerformed(ActionEvent evt)
{
Object object1 = evt.getSource();
if (object1 == startButton)
{
mandelRect = new MandelRect (width, height);
start();
}
if (object1 == zoomButton)
{
mandelRect.setPaintRect (false);
mandelRect.scaleSet ();
start();
}
}
}
PS:
As I don't know how to make java applets, it would be nice if someone could make an applet out of the above source just to see the effect on the mandelbrot set.