/*----------------------------------------------------------------------+ | Title: MandelbrotColorPlane.java | | | | Author: David E. Joyce | | Department of Mathematics and Computer Science | | Clark University | | Worcester, MA 01610-1477 | | U.S.A. | | | http://aleph0.clarku.edu/~djoyce/ | | | | Date: January, 2003. | +----------------------------------------------------------------------*/ import java.awt.Color; public class MandelbrotColorPlane extends ColorPlane { private int resolution; private int parPlane; private int wrap; private int escape; private int pattern; private double bound, bound2; public static final double MYERBERG = 1.401155189; public static final int MU = 0; public static final int LAMBDA = 1; // mu = lambda^2/4 - lambda/2 public static final int RECIPMU = 2; // 1/mu public static final int RECIPMUPLUSFOURTH = 3; // 1/(mu+.25) public static final int RECIPLAMBDA = 4; // 1/lambda public static final int RECIPLAMBDAMINUSONE = 5; // 1/(lambda-1) public static final int RECIPMUMINUSMYER = 6; // 1/(mu-1.401) public static final int CIRCLE_ESCAPE = 0; public static final int SQUARE_ESCAPE = 1; public static final int STRIP_ESCAPE = 2; public static final int HALFPLANE_ESCAPE = 3; public static final int PLAIN_PATTERN = 0; public static final int FEATHERED_PATTERN = 1; public static final int BINARY_PATTERN = 2; public static final int GRAYSCALE_PATTERN = 3; public static final int ZEBRA_PATTERN = 4; private static final double[] BOUND_VALUE = {2.0, 20.0, 5.0, 2.0, 5.0}; public MandelbrotColorPlane (int resolution, int parPlane, int wrap, int escape, int pattern) { setResolution(resolution); setParPlane(parPlane); setWrap(wrap); setEscape(escape); setPattern(pattern); } public void setResolution (int resolution) { this.resolution = resolution; } public void setParPlane (int parPlane) { this.parPlane = parPlane; } public void setWrap (int wrap) { this.wrap = wrap; } public void setEscape (int escape) { this.escape = escape; } public void setPattern (int pattern) { this.pattern = pattern; bound = BOUND_VALUE[pattern]; bound2 = bound*bound; } public static Complex convert (Complex z, int oldPlane, int newPlane) { if (oldPlane == newPlane) return new Complex(z); // first convert from the old plane to either the lambda or the mu plane if (oldPlane != MU && oldPlane != LAMBDA) { z = z.reciprocal(); if (oldPlane == RECIPMUPLUSFOURTH) z = z.minus(0.25); else if (oldPlane == RECIPLAMBDAMINUSONE) z = z.plus(1.0); else if (oldPlane == RECIPMUMINUSMYER) z = z.plus(MYERBERG); } // if // next, convert to mu or lambda as necessary if (oldPlane==LAMBDA || oldPlane==RECIPLAMBDA || oldPlane==RECIPLAMBDAMINUSONE) { if (newPlane!=LAMBDA && newPlane!=RECIPLAMBDA && newPlane!=RECIPLAMBDAMINUSONE) { // convert lambda to mu. mu = (lambda/2)^2 - (lambda/2) z = z.over(2.0); z = z.times(z).minus(z); } // if } else { if (newPlane==LAMBDA || newPlane==RECIPLAMBDA || newPlane==RECIPLAMBDAMINUSONE) { // convert mu to lambda. lambda = 1 + sqrt(1+4mu) z = z.times(4.0).plus(1.0); z = z.sqrt().plus(1.0); } // if } // if/else // finally, convert to the new plane switch (newPlane) { case MU: return z; case LAMBDA: return z; case RECIPMU: return z.reciprocal(); case RECIPMUPLUSFOURTH: return z.plus(0.25).reciprocal(); case RECIPLAMBDA: return z.reciprocal(); case RECIPLAMBDAMINUSONE: return z.minus(1.0).reciprocal(); case RECIPMUMINUSMYER: return z.minus(MYERBERG).reciprocal(); } // switch return z; // never used, but makes compiler happy } // convert private boolean escaped (double s, double t) { switch (escape) { case CIRCLE_ESCAPE: return s*s + t*t >= bound2; case SQUARE_ESCAPE: return Math.abs(s) >= bound || Math.abs(t) >= bound; case STRIP_ESCAPE: return Math.abs(s) >= bound; case HALFPLANE_ESCAPE: return s >= bound; } // switch return true; // never used, but makes compiler happy } // escaped // Determine whether the point z=x+iy lies in the Mandelbrot set or not. public Color valueAt (double x, double y) { Complex z = new Complex(x,y); // First, change (x,y) to be a point in the mu-plane, if it isn't in it yet. z = convert(z,parPlane,MU); x = z.x; y = z.y; // Next check to see if z + 1/4 lies inside the cardioid // given by the polar inequality r < (1+cos theta)/2. In rectangular // coordinates this becomes (s^2 + t^2 - s/2)^2 < (s^2 + t^2)/4 where // (s,t) is (x + 1/4, y). If so, z lies inside the set double s = x + 0.25; double t = y; double ss = s*s; double tt = y*y; double left = (ss + tt - s*0.5); if (left*left < (ss + tt)*0.25) return Color.black; // Now see how many iterates of 0 it takes to reach the escape criterion // These computations do not use the Complex class for sake of speed s = 0.0; t = 0.0; int k = 0; for (k = 0; !escaped(s,t) && k < resolution; k++) { ss = s*s - t*t; tt = 2.0 * s * t; s = ss - x; t = tt - y; } // for if (k == resolution) return Color.black; if (pattern == ZEBRA_PATTERN) return (k%2 == 0)? Color.black : Color.white; if (pattern == FEATHERED_PATTERN) { if (Math.abs(s) 0) hue = (hue<0.8)? hue+0.2 : hue-0.8; int cidx = Color.HSBtoRGB ((float)(hue), 1.0f, 1.0f); return new Color(cidx); } public String toString() { return "["+resolution+","+parPlane+","+wrap+","+escape+","+pattern+"]" ; } // toString } // MandelbrotColorPlane