/*********************************************************
* *
* Fractal Generating Applet *
* *
*********************************************************/
/* DiBiano, Robert J.
* Fractal Generating Applet
* 11/8/2002
*
* Purpose: graphically show what root complex numbers converge to with Newton's method.
*
* The applet can recive non default varibale values from the calling web page through
* the use of the param command as shown in this sample applet call:
*
*
*
* Valid Parameters:
* - width and height are in pixels, name and value are strings, quotes are optional though
* - polynomial is the polynomial
* - xMin,xMax,yMin,yMax are bounds of graph
* - lowIter: if it converges in this many or less will look a shade darker
* - highIter: if it takes this many or more to converge, graph will be a shade lighter
*
*
* Changes Log:
*
* 11/08/2002 - changes log started
* 11/21/2002 - formula for yStepSize was wrong (was same as for xStepSize) so I fixed it.
* 12/17/2003 - was displaying upside down(fixed I tihnk), added interface for end user to
* type in the function to use and various other data. No error checking right now.
*
*
* Known Issues: The program is still in it's early stages, so error checking may not exist
* for everything.
* - not sure it can handle negative height and width input
* - polynomial can only be in standard form (Ax^B + Cx^D + ...) and only integer coefficients
* - class polynomial is a hunk of junk; string input has to be redone minimally (so floats
* work), internal representation (and therefore derivative method) could stand to be
* totally re worked, it would also be nice to handle stuff in factored form or functions
* other than simple powers.
* - xMin, xMax, yMin, yMax also can only be integers if brought from outside b/c we use
* parseInt to parse them, browsers don't support the java version that that has
* parseDouble yet.
* - Our test for convergence does not actually work in some cases it seems. Things like x^5
* show up as having many different roots b/c roots are badly calculated after a false
* convergence detection flag is raised. There may be other problems obscured by this
* one.
* - only the first 10 roots get unique colors, eventually want to generate random colors
* beyond this.
*
*/
import java.applet.*;
import java.awt.*;
import java.lang.Math;
public class atractor extends Applet
{
String polyString="x^3-1"; // polynomial to do Newton's method on
polynomial equation; // wrapper class for polynomial
polynomial derivative; // d(polynomial)/dx
int width=100, height=100; // image width and height in pixels
double xMin=-1, xMax=1, yMin=-1, yMax=1; // graph bounds
double xStepSize=(xMax-xMin)/(width-1); // x step size between pixels
double yStepSize=(yMax-yMin)/(height-1); // y step size between pixels
double error=Math.min(xStepSize,yStepSize)/100; // max error for several things
int lowIter=0, highIter=0;
Font font=new Font("Courier", Font.PLAIN, 20);
Font bigFont=new Font("Courier", Font.BOLD, 25);
boolean fractalDrawn=false; // is fractal drawn once yet?
int maxRoots=10; // max number of individual roots displayable
complex roots[]=new complex[maxRoots]; // roots of our equation
Color colors[]=new Color[maxRoots+1]; // colors for each root
Image offScrImage; // an offscreen scratch pad to hold the image
Graphics offScrGC; // the graphics context used to draw on the pad
Button b1;
TextField tPolynomial,tXMin,tXMax,tYMin,tYMax,tLowIter,tHighIter;
public void init()
{
String parameter; // holds external data fed into applet
for (int x=0; x=highIter) temp=temp.brighter();
}
offScrGC.setColor(temp); // set to correct color
g.setColor(temp);
offScrGC.drawLine(xPixel,yPixel,xPixel,yPixel); // plot the point
g.drawLine(xPixel,yPixel,xPixel,yPixel);
} // End of if (converged)
}
} // End of 2 loops to iterate through every pixel
fractalDrawn=true;
} // End of draw fractal
else
{
// stick our prefabricated fractal onto the applets graphics context,
// this way we dont need to re-generate the fractal in order to re display it
g.drawImage(offScrImage,0,0,width,height,this);
}
} // END of paint
}// END of class atractor
//----------------------------------------------------------------------------------------------
class complex // implements complex numbers, needs java.lang.Math
{
private double realPart;
private double imaginaryPart;
public complex(double realPart, double imaginaryPart) // constructor
{
this.realPart=realPart;
this.imaginaryPart=imaginaryPart;
}
public complex() // constructor
{
this.realPart=0;
this.imaginaryPart=0;
}
void setEqual(complex other) // = operator
{
this.realPart=other.realPart;
this.imaginaryPart=other.imaginaryPart;
}
void setEqual(double realPart, double imaginaryPart) // = operator
{
this.realPart=realPart;
this.imaginaryPart=imaginaryPart;
}
complex add(complex other) // + operator
{
complex answer=new complex();
answer.realPart=this.realPart+other.realPart;
answer.imaginaryPart=this.imaginaryPart+other.imaginaryPart;
return answer;
}
complex add(double other) // + operator
{
complex answer=new complex();
answer.realPart=this.realPart+other;
answer.imaginaryPart=this.imaginaryPart;
return answer;
}
complex subtract(complex other) // - operator
{
complex answer=new complex();
answer.realPart=this.realPart-other.realPart;
answer.imaginaryPart=this.imaginaryPart-other.imaginaryPart;
return answer;
}
complex subtract(double other) // - operator
{
complex answer=new complex();
answer.realPart=this.realPart-other;
answer.imaginaryPart=this.imaginaryPart;
return answer;
}
complex multiply(complex other) // * operator
{
complex answer=new complex();
answer.realPart=this.realPart*other.realPart-this.imaginaryPart*other.imaginaryPart;
answer.imaginaryPart=this.realPart*other.imaginaryPart+this.imaginaryPart*other.realPart;
return answer;
}
complex multiply(double other) // * operator
{
complex answer=new complex();
answer.realPart=this.realPart*other;
answer.imaginaryPart=this.imaginaryPart*other;
return answer;
}
complex divide(complex other) // / operator
{
complex answer=new complex();
answer.realPart=(this.realPart*other.realPart+this.imaginaryPart*other.imaginaryPart)/
(other.realPart*other.realPart+other.imaginaryPart*other.imaginaryPart);
answer.imaginaryPart=(this.imaginaryPart*other.realPart-this.realPart*other.imaginaryPart)/
(other.realPart*other.realPart+other.imaginaryPart*other.imaginaryPart);
return answer;
}
complex divide(double other) // / operator
{
complex answer=new complex();
answer.realPart=this.realPart/other;
answer.imaginaryPart=this.imaginaryPart/other;
return answer;
}
complex pow(int n) // raises to the nth power
{
if (n>1) return this.multiply(this.pow(n-1));
if (n==1) return this;
complex one = new complex(1,0);
if (n==0) return one;
return one.divide(this.pow(-n));
}
public String toString() // automatic string conversion
{
String s;
s = realPart + "+" + imaginaryPart + "i";
return s;
}
public boolean isNear(double range, complex other) //true if #'s are within range of each other
{
double distance=Math.sqrt((this.realPart-other.realPart)*(this.realPart-other.realPart)+
(this.imaginaryPart-other.imaginaryPart)*(this.imaginaryPart-other.imaginaryPart));
if (distance<=range) return true;
return false;
}
public boolean isNear(double range, double other) // true if #'s are within range of each other
{
double distance=Math.sqrt((this.realPart-other)*(this.realPart-other)+
this.imaginaryPart*this.imaginaryPart);
if (distance<=range) return true;
return false;
}
} // End of class complex
//----------------------------------------------------------------------------------------------
class polynomial // class to hold a polynomial in standard form only, NOT especially robust
{
private int maxTerms=10;
private String equation;
private int terms;
private int multipliers[]=new int[maxTerms];
private int coefficients[]=new int[maxTerms];
private boolean isANumber(char c)
{
return c>='0' && c<='9';
}
private boolean isValid(char c)
{
return isANumber(c) || c=='x' || c=='X' || c=='^' || c=='+' || c=='-';
}
public polynomial(String s) // constructor
{
int x,length;
char c;
boolean working; // are we working on a term?
equation=s;
equation.trim();
equation.toLowerCase();
equation+=' ';
length=equation.length();
terms=0; // # of terms and location of unfinished term
working=false;
for (x=0;x=0) multipliers[terms]+=(int)equation.charAt(x)-48;
else multipliers[terms]-=(int)equation.charAt(x)-48;
x+=1;
}
if (equation.charAt(x)=='x' || equation.charAt(x)=='X') // stuff past a 'x'
{
x+=1;
coefficients[terms]=1;
if (equation.charAt(x)=='^') // stuff past a '^'
{
x+=1;
while (equation.charAt(x)=='+' || equation.charAt(x)=='-')
{
if (equation.charAt(x)=='-') coefficients[terms]=-coefficients[terms];
x+=1;
}
if (isANumber(equation.charAt(x)))
{
coefficients[terms]*=(int)equation.charAt(x)-48;
x+=1;
}
while (isANumber(equation.charAt(x)))
{
coefficients[terms]*=10;
if (coefficients[terms]>=0) coefficients[terms]+=(int)equation.charAt(x)-48;
else coefficients[terms]-=(int)equation.charAt(x)-48;
x+=1;
}
} // End of stuff past a '^'
} // End of stuff past a 'x'
terms+=1; // term finished
x-=1; // go back on char b/c loop will advance by one
} // End of valid term
} // End of traverse valid chars
} // End of traverse eqation
} // End of constructor
public String toString() // automatic string conversion
{
int x;
String s="";
for (x=0;x