package fractal;
import java.awt.*;
import fractal.utils.*;
abstract class FractalCalculator implements Runnable
{
protected Color[]
colorMap;
protected int[][]
colorNumbers;
protected double
delta;
protected Fractal
fractal;
protected Image
image;
protected double
iRangeMax;
protected double
iRangeMin;
protected int
maxIterations;
protected Drawing
newDrawing;
protected ComplexRectangle newRect;
protected int
numColors;
protected int
imageHeight;
protected int
imageWidth;
protected double
rRangeMax;
protected double
rRangeMin;
protected boolean
stopRequested;
protected FractalCalculator( Fractal fractal, Drawing newDrawing
)
{
this.fractal = fractal;
this.newDrawing = newDrawing;
image
= newDrawing.getImage();
maxIterations = newDrawing.getMaxIterations();
newRect
= newDrawing.getComplexRect();
imageWidth = fractal.getImageWidth();
imageHeight = fractal.getImageHeight();
colorMap
= fractal.getCurrentColorMap();
numColors
= colorMap.length;
rRangeMin
= newRect.getRMin();
rRangeMax
= newRect.getRMax();
iRangeMin
= newRect.getIMin();
iRangeMax
= newRect.getIMax();
delta
= (rRangeMax - rRangeMin) / (double) imageWidth;
colorNumbers = null; // set
this up later.
stopRequested = false;
}
private boolean calcFractal()
{
// Assign a color to every pixel ( x , y ) in
the Image, corresponding to
// one point, z, in the imaginary plane ( zr,
zi ).
Graphics imageGraphics = null;
try
{
colorNumbers = getColorNumbers();
fractal.setStatus2( " 0% Complete."
);
// Get the Graphics object for the
Image. This is necessary
// for "double buffering" of the
calculated image.
imageGraphics = image.getGraphics();
imageGraphics.setPaintMode();
// For each pixel...
int loopCounter = 0;
for( int x = 0; x < imageWidth;
x++ )
{
for( int y = 0; y <
imageHeight; y++ )
{
Color c
= getColor( x, y );
imageGraphics.setColor(
c );
imageGraphics.drawLine(
x, y, x, y );
if( ! maybeYieldOrStop(
++loopCounter ) )
{
return false; // stop was requested.
}
}
fractal.setStatus2(
" " + 100 * x / imageWidth + "% Complete." );
}
newDrawing.setColorNumbers( colorNumbers
);
fractal.setStatus2( " 100% Complete."
);
return true;
}
catch( OutOfMemoryError oom )
{
fractal.outOfMemory( true );
return false;
}
catch( Throwable t )
{
System.out.println( "Fractal ERROR
!!! (calc fractal) ... " + t );
return false;
}
finally
{
if( imageGraphics != null )
{
imageGraphics.dispose();
// garbage.
}
}
}
protected Color getColor( int x, int y )
{
Color c = Color.black;
colorNumbers[ x ][ y ] = -1; // -1 indicates
black.
double zR = rRangeMin + ((double) x ) * delta;
double zI = iRangeMin + ((double)( imageHeight
- y )) * delta;
// Is the point inside the set?
int numIterations = testPoint( zR, zI, maxIterations
);
if( numIterations != 0 )
{
// The point is outside the
set. It gets a color based on the number
// of iterations it took to
know this.
int colorNum = (int)((float)
numColors * ( 1.0 -
(float) numIterations / (float) maxIterations ));
colorNum = (colorNum == numColors)
? 0 : colorNum;
// Save this information, to
the slight detriment of this calculator's
// speed, in order to greatly
increase the performance for creating
// future Drawings, when only
the color scheme has been changed.
colorNumbers[ x ][ y ] = colorNum;
c = colorMap[ colorNum ];
}
return c;
}
protected int[][] getColorNumbers()
{
// Beware: out of memory! Save the colorNumber
data with the drawing
// in order to be able to use the FastColorsCalculator
later on.
return new int[ imageWidth ][ imageHeight ];
}
protected String getConsoleOutputString()
{
return new String( "Calculating new fractal."
);
}
protected boolean maybeYieldOrStop( int loopCounter )
{
// Improve the response time for the UI (to
the slight detriment of this
// Thread's speed). Suggest to the Virtual Machine's
Thread scheduler that
// it yield processing to other Threads.
if( loopCounter % 2048 == 0 )
{
Thread.yield(); // Needed for UI
performance on some browsers.
if( stopRequested ) // Did the user
press the Stop button?
{
return false;
}
}
return true;
}
public void run()
{
// This is the entry point for the new Thread,
called after
// the parent Thread calls Thread.start().
try
{
System.out.println( " " );
System.out.println( "Starting new
drawing: " );
System.out.println( "Zoom factor:
" + (long)fractal.getZoomFactor() );
newDrawing.dump();
System.out.println( getConsoleOutputString()
);
if( calcFractal() )
{
System.out.println(
"Drawing completed." );
fractal.calculatorCallback(
true, newDrawing );
}
else
{
// Drawing stopped for
some reason or another.
System.out.println(
"Drawing stopped." );
fractal.calculatorCallback(
false, null );
}
}
catch( OutOfMemoryError oom )
{
fractal.outOfMemory( true );
fractal.calculatorCallback( false,
null );
}
catch( Throwable t )
{
System.out.println( "Fractal ERROR
!!! (calculator) ... " + t );
fractal.calculatorCallback( false,
null );
}
}
protected void stop()
{
stopRequested = true;
}
// Subclasses must implement this method:
protected abstract int testPoint( double r, double i, int
maxIterations );
}