/* 9 Mar 07 based on Wolf Jung's sources http://www.mndynamics.com/indexp.html Thanks to Michael Ganss for the BT algorithm description URL: user.cs.tu-berlin.de/~rms/AlmondBread/ */ import java.awt.*; import java.awt.image.*; import java.awt.event.*; import java.util.StringTokenizer; public class ERays extends java.applet.Applet implements MouseListener { double maxIZI2=4., Zoom=2., Ymid=.0, Xmid=-.5, DelX=3., paramC, paramD; int MaxIt = 256, maxColor = 96, q = 1, culculatedPix, w, h, pixArr[]; Image img; IndexColorModel RainbowColor; long generTime; boolean showXY = true; FormulaR formR; TextField tfXY; public void init() { String s=getParameter("XYmidDelCD"); if (s != null) { StringTokenizer st = new StringTokenizer(s); Xmid = Double.valueOf(st.nextToken()).doubleValue(); Ymid = Double.valueOf(st.nextToken()).doubleValue(); DelX = Double.valueOf(st.nextToken()).doubleValue(); paramC = Double.valueOf(st.nextToken()).doubleValue(); paramD = Double.valueOf(st.nextToken()).doubleValue();} s=getParameter("Zoom"); if (s != null) Zoom=Double.valueOf(s).doubleValue(); s=getParameter("MaxIt"); if (s != null) MaxIt=Integer.parseInt(s); s=getParameter("q"); if (s != null) q=Integer.parseInt(s); s=getParameter("MaxColor"); if (s != null) maxColor=Integer.parseInt(s); byte rColor[] = new byte[maxColor+2], bColor[] = new byte[maxColor+2], gColor[] = new byte[maxColor+2]; Color c; for (int i = 0; i < maxColor; i++){ c = Color.getHSBColor(((float)i)/maxColor, 1f, .8f); rColor[i] = (byte)c.getRed(); gColor[i] = (byte)c.getGreen(); bColor[i] = (byte)c.getBlue();} rColor[maxColor+1] = gColor[maxColor+1] = bColor[maxColor+1] = (byte)255; RainbowColor = new IndexColorModel( 8, maxColor+2, rColor,gColor,bColor); w = getSize().width; h = getSize().height; s=getParameter("showXY"); if ( (s != null) && (s.equalsIgnoreCase("N")) ) showXY = false; else{ setLayout(new BorderLayout()); if (Ymid < 0.) s = ""+Xmid+" "+Ymid+"*i; "+(float)DelX; else s = ""+Xmid+" +"+Ymid+"*i; "+(float)DelX; tfXY = new TextField( s ); add("South", tfXY); h -= tfXY.getPreferredSize().height;} pixArr = new int[w*(h+2)]; s=getParameter("Formula"); if (s != null) try{ formR = (FormulaR)Class.forName( s ).newInstance(); }catch(Exception e){} else formR = new JRay(); formR.set(MaxIt, maxColor, q); generTime = System.currentTimeMillis(); BTracing(Xmid, Ymid, DelX/w, formR, paramC, paramD ); img = createImage(new MemoryImageSource(w, h, RainbowColor, pixArr, w, w)); generTime = System.currentTimeMillis()-generTime; addMouseListener(this); } public void destroy() { removeMouseListener(this); } public void mouseClicked(MouseEvent e) {} //1.1 event handling public void mousePressed(MouseEvent e) {} public void mouseEntered(MouseEvent e) {} public void mouseExited(MouseEvent e) {} public void mouseReleased(MouseEvent e) { Xmid = Xmid+(e.getX()-w/2)*DelX/w; Ymid = Ymid-(e.getY()-h/2)*DelX/w; if ( e.isControlDown() ) { if ( e.isShiftDown() ) DelX *=Zoom*Zoom; else DelX *=Zoom;} else { if ( e.isShiftDown() ) DelX /=Zoom*Zoom; else DelX /=Zoom;} generTime = System.currentTimeMillis(); BTracing(Xmid, Ymid, DelX/w, formR, paramC, paramD); img.flush(); img = createImage(new MemoryImageSource(w, h, RainbowColor, pixArr, w, w)); generTime = System.currentTimeMillis()-generTime; if (showXY) { if (Ymid < 0.) tfXY.setText( ""+Xmid+" "+Ymid+"*i; "+(float)DelX); else tfXY.setText( ""+Xmid+" +"+Ymid+"*i; "+(float)DelX);}; repaint(); } public void BTracing( double Xmid, double Ymid, double Step, FormulaR form, double parC, double parD){ final int dirRight=0, dirUP = 3, maskDir=3, culcColor=-1, borderColor=-2; for (int i=0; i < w; i++) pixArr[i] = pixArr[i+w*(h+1)] = borderColor; for (int i=w; i < w*(h+1); i++) pixArr[i] = culcColor; for (int i=w; i < w*(h+2); i+=w) pixArr[i] = pixArr[i-1] = borderColor; double X,Y, delX[] = {Step,0.,-Step,0.}, delY[] = {0.,-Step,0.,Step}; int pix=w+1, iy,ix, curDir, curPix, startPix, startColor, nextColor, delPix[] = {1,w,-1,-w}; culculatedPix = h*(w-2); for (iy=0, Y=Ymid+Step*h/2; iy < h; iy++, Y-=Step, pix+=2) { for (ix=1, X=Xmid-Step*(w/2-1); ix < w-1; ix++, X+=Step, pix++) { if ( pixArr[pix] == culcColor ) { double curX = X, curY = Y, nextX, nextY; curPix = startPix = pix; curDir = dirRight; startColor = pixArr[pix] =form.iterate(X, Y, parC, parD); while ( pixArr[startPix - w] == startColor ){ // looking for boundary curPix = startPix = startPix - w; curY +=Step;} do { // tracing cycle for (int Dir = curDir+3; Dir < curDir+7; Dir++) { int dir = Dir & maskDir; int nextPix = curPix + delPix[dir]; nextX = curX+delX[dir]; nextY = curY+delY[dir]; if ( (nextColor = pixArr[nextPix]) == culcColor ) nextColor = pixArr[nextPix] = form.iterate(nextX, nextY, parC, parD); if ( nextColor == startColor ) { curDir = dir; curPix = nextPix; curX = nextX; curY = nextY; break; } } } while ( curPix != startPix ); curDir = dirRight; do { // 2nd cycle for (int Dir = curDir+3; Dir < curDir+7; Dir++) { int dir = Dir & maskDir; int nextPix = curPix + delPix[dir]; if ( pixArr[nextPix] == startColor ) { // flooding curDir = dir; if (dir == dirUP) { int floodPix = curPix, floodColor; while ( (floodColor = pixArr[++floodPix]) == culcColor || floodColor == startColor){ pixArr[floodPix] = startColor; culculatedPix--;} } curPix = nextPix; break; } } } while ( curPix != startPix ); } } } for (int i=w; i < w*(h+1); i+=w) { pixArr[i] = pixArr[i+1]; pixArr[i+w-1] = pixArr[i+w-2];} culculatedPix = (culculatedPix*100+w*h/2)/(w*h); } public void paint(Graphics g) { g.drawImage(img, 0, 0, this); drawLabel(g); showStatus( "Time=" + generTime + " msec, Culculated =" + culculatedPix + "%"); } public void drawLabel(Graphics gra) { double StZ=DelX/w; gra.setColor(Color.white); String s=getParameter("sqrRID"); if (s != null) { StringTokenizer st = new StringTokenizer(s); double sqrRmid = Double.valueOf(st.nextToken()).doubleValue(), sqrImid = Double.valueOf(st.nextToken()).doubleValue(); int sqr = (int)(Double.valueOf(st.nextToken()).doubleValue()/StZ); int x = (w - sqr)/2 + (int)((sqrRmid-Xmid)/StZ); int y = (h - sqr)/2 + (int)((Ymid - sqrImid) /StZ); gra.drawRect(x,y, sqr,sqr);} gra.setFont( new Font( gra.getFont().getName(), Font.BOLD, 15 ) ); int maxLabel=0, w2 = w/2, h2 = h/2; double dw2 = w2, dh2 = h2; while (true) { s=getParameter("lb"+maxLabel); if (s == null) break; StringTokenizer st = new StringTokenizer(s); double x=(Double.valueOf(st.nextToken()).doubleValue()-Xmid)/StZ, y=(Ymid-Double.valueOf(st.nextToken()).doubleValue())/StZ; if ( (Math.abs(x) 0 && (y*X - x*Y)*(Ci*X - Cr*Y) > 0 && ((Ci-y)*(Cr-X) - (Cr-x)*(Ci-Y))*(Cr*Y - Ci*X) > 0) { if (u < 0) u += 2*Math.PI; else u -= 2*Math.PI; } theta += s*u; if (r > 1e18*s) break; } if (r < 1000) return maxColor; // ?? theta *= (.5/Math.PI); theta -= Math.floor(theta); return (int)(theta * q) % maxColor; } } class MRay extends FormulaR{ public int iterate(double Cr, double Ci, double p1, double p2) { double s = 1, dr = 1.0/2, x, y, r, theta, // external angle u, v; // t=u+v*i x = .25 - Cr; y = - Ci; v = Math.sqrt(x*x + y*y); u = 0.5*(v + x); v = 0.5*(v - x); if (u < 0) u = 0; if (v < 0) v = 0; //safer when y = 0 u = Math.sqrt(u); v = Math.sqrt(v); if (y < 0) v = -v; double X = .5 - u, Y = -v; x = Cr; y = Ci; r = x*x + y*y; if (r < 1e-8) return maxColor; // ?? theta = Math.atan2(y, x); for (int j = 1; j < MaxIt; j++){ s *= dr; u = x*x - y*y; v = 2*x*y; x = u + Cr; y = v + Ci; r = x*x + y*y; if (r < 1e-8) return maxColor; // ?? u = Math.atan2(u*y - v*x, u*x + v*y); if ( (y*Cr - x*Ci)*(Y*Cr - X*Ci) > 0 && (y*X - x*Y)*(Ci*X - Cr*Y) > 0 && ((Ci-y)*(Cr-X) - (Cr-x)*(Ci-Y))*(Cr*Y - Ci*X) > 0) { if (u < 0) u += 2*Math.PI; else u -= 2*Math.PI; } theta += s*u; if (r > 1e18*s) break; } if (r < 1000) return maxColor; // ?? theta *= (.5/Math.PI); theta -= Math.floor(theta); return (int)(theta * q) % maxColor; } } class Tan extends FormulaR{ public int iterate(double re, double im, double cr, double ci) { double I=im, R=re, I2=I*I, R2=R*R, t; int n=0; do { if (R2+I2 > 100.) break; I=(R+R)*I+ci; R=R2-I2+cr; R2=R*R; I2=I*I; n++; } while ( n < MaxIt ); if (n == MaxIt) return maxColor; t = .5*Math.atan2(I,R)/Math.PI; t -= Math.floor(t); return (int)(maxColor*t); } }