I have a great interest in fractals but did not have the opportunity to implement them until recently. I first implemented a mandelbrot set in black and white then I tried to add colors to it.
Here is my mandelbrot's implementation (I am using org.apache.commons.math3.complex.Complex for complex numbers)
public class MyMandelbrot {
public static int numberOfIterationsToCheck(Complex z0, int max) {
Complex z = z0;
for (int t = 0; t < max; t++) {
if (z.abs() > 2.0) return t;
z =z.multiply(z).add(z0);
}
return max;
}
public static void main(String[] args) {
double xc = Double.parseDouble(args[0]);
double yc = Double.parseDouble(args[1]);
double size = Double.parseDouble(args[2]);
int N = 512;
int max = 255;
Viewer viewer = new Viewer(N, N);
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
double x0 = xc - size/2 + size*i/N;
double y0 = yc - size/2 + size*j/N;
Complex z0 = new Complex(x0, y0);
int gray = max - numberOfIterationsToCheck(z0, max);
Color color = new Color(gray, gray, gray);
if (z0.abs() > 2.0 ) {
color = new Color(gray, 128, gray);
} else if (z0.abs() > 2.0 && numberOfIterationsToCheck(z0, max) > max/2) {
color = new Color(255, gray, 255);
} else if (z0.abs() > 2.0 && numberOfIterationsToCheck(z0, max) < max/2) {
color = new Color(gray, 128,128);
}
else if (z0.abs() > 1.0 && numberOfIterationsToCheck(z0, max) < max/2 ) {
color = new Color(128, gray, 128);
} else if (z0.abs() > 1.0) {
color = new Color(128, gray, 128);
}
else if (z0.abs() <= 1.0) {
color = new Color(gray, gray, 128);
}
viewer.set(i, N-1-j, color);
}
}
viewer.show();
}
}
I am using a custom Viewer class to view the set after drawing it in an image object. Here is the set method of the Viewer
public void set(int col, int row, Color color) {
if (col < 0 || col >= width()) throw new IndexOutOfBoundsException("col must be between 0 and " + (width()-1));
if (row < 0 || row >= height()) throw new IndexOutOfBoundsException("row must be between 0 and " + (height()-1));
if (color == null) throw new NullPointerException("can't set Color to null");
if (isOriginUpperLeft) image.setRGB(col, row, color.getRGB());
else image.setRGB(col, height - row - 1, color.getRGB());
}
The code is rendering the set correctly but I am not obtaining the expected result. What I want is to be able to produce a colored set similar to these
Or this
But I could not get a better colored set than this.
I have read some theorical explanation about it here and here, but I am obviously doing something wrong in practice. What is wrong with my coloring approach? How can I fix it? Thanks
In the examples you show, the color is just based on the number of iterations before the point escapes, and not on the initial complex co-ordinate z0. One approach is to use Hue-Saturation-Brightness color values using getHSBColor(), and change the hue based on the number of iterations before it escapes, e.g:
double x0 = xc - size/2 + size*i/N;
double y0 = yc - size/2 + size*j/N;
Complex z0 = new Complex(x0, y0);
int escapeIterations = numberOfIterationsToCheck(z0, max);
// set color varying hue based on escape iterations:
Color color = Color.getHSBColor((float)escapeIterations / (float)max, 1.0f, 1.0f);
viewer.set(i, N-1-j, color);
The code above doesn't change the saturation or the brightness (both are set to 1.0) but you could vary them as well depending on what kind of effect you want.
You could make the color cycle through the color circle of hues more than once by multiplying the hue value by a constant e.g:
(float)escapeIterations * 2.0f / (float)max
You can also add a constant to make it start at a particular color.
Because escapeIterations
is an int, the color will jump in steps for each iteration. You can make the colors smoother by returning a float from numberOfIterationsToCheck
:
public static float numberOfIterationsToCheckSmooth(Complex z0, int max) {
Complex z = z0;
for (int t = 0; t < max; t++) {
double fAbs = z.abs();
if (fAbs > 2.0)
{
// based on the final value, add a fractional amount based on
// how much it escaped by (fAbs will be in the range of 2 to around 4):
return (float)t + (2.0f - (Math.log(fAbs) / Math.log(2.0)));
}
z =z.multiply(z).add(z0);
}
return (float)max;
}
Lastly, another approach that will give the most freedom and control over the colors is to use a table of colors, one for each iteration up the maximum, and optionally interpolate between them.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With