//------------------------------------------------------------------------------
// MandelbrotPanel.java (Mandelbrot Applet 1.07)
// Copyright (C) 2001, 2014 by Alexander Adam
// Tested with Java 8
// No Warranty of any kind.
//------------------------------------------------------------------------------
package de.alexanderadam;
import java.awt.*;
import java.awt.event.*;
/**
* MandelbrotPanel
* Handles a Panel that calculates and paints mandelbrot sets
*/
public class MandelbrotPanel extends Panel {
private static final long serialVersionUID = 4912220800844778957L;
private Image image;
private Graphics graphics;
private Mandelbrot mandelbrot;
private boolean keepProportions = true;
private boolean areaSelected = false;
private boolean areaSelectionEnabled = false;
//
// mandelbrot set (initial) parameters
//
private double x0 = -0.5f; // c0: real part (x)
private double y0 = 0.0f; // c0: imaginary part (y)
private int size_x; // (image) size - width
private int size_y; // (image) size - height
private int size_x2; // for size_x / 2
private int size_y2; // for size_y / 2
private int colors = 256; // max calculation depth
private float super_hue = 0.0f;
private float hue_offset = 0.0f;
private float hue_range = 1.0f;
private double range_x = 3.2f; // calculation x-Range
private double range_y = 2.4f; // calculation y-Range
private double prec_x; // calculation steps ( precision_x )
private double prec_y; // calculation steps ( precision_y )
//
// selected area varaiables
//
private int selX0;
private int selY0;
private int selX1;
private int selY1;
MandelbrotPanel( Mandelbrot parent ) {
super();
mandelbrot = parent;
if (mandelbrot != null) {
addMouseListener( new MandelbrotMouseListener() );
addMouseMotionListener( new MandelbrotMouseMotionListener() );
}
}
MandelbrotPanel( Mandelbrot parent, double c0x, double c0y,
double rangeX, double rangeY, int maxCol,
boolean prop ) {
super();
mandelbrot = parent;
x0 = c0x;
y0 = c0y;
range_x = rangeX;
range_y = rangeY;
colors = maxCol;
keepProportions = prop;
if (mandelbrot != null) {
addMouseListener( new MandelbrotMouseListener() );
addMouseMotionListener( new MandelbrotMouseMotionListener() );
}
}
//
// public getter functions
//
public double getX0() {
return x0;
}
public double getY0() {
return y0;
}
public double getRangeX() {
return range_x;
}
public double getRangeY() {
return range_y;
}
public int getColors() {
return colors;
}
public boolean getProp() {
return keepProportions;
}
public float getHueOffset() {
return hue_offset;
}
public float getHueRange() {
return hue_range;
}
public float getSuperHue() {
return super_hue;
}
//
// public setter functions
//
public void setX0( double x ) {
x0 = x;
}
public void setY0( double y ) {
y0 = y;
}
public void setRangeX( double x ) {
range_x = x;
}
public void setRangeY( double y ) {
range_y = y;
}
public void setColors( int c ) {
colors = c;
}
public void setProp( boolean prop ) {
keepProportions = prop;
}
public void setHueOffset( float hue ) {
hue_offset = hue;
}
public void setHueRange( float hue ) {
hue_range = hue;
}
public void setSuperHue( float hue ) {
super_hue = hue;
}
/*
* Gets width and height to call calculate Mandelbrot Set
*/
public void calculate() {
int sx = getSize().width; // get width
int sy = getSize().height; // get height
if (sx > 0 && sy > 0 ) {
calculate(sx,sy);
}
}
/**
* Calculate Mandelbrot Set
*
* @param sx = height
* @param sy = width
*
* see James Gleick - CHAOS, page 231ff
*
* z' = z^2 + c
* c = x + yi ( complex number )
* z0 = 0
*
* -> z' = x + yi
* -> z'' = ( x' + y'i )^2 + x + yi
* = x'^2 + 2x'y'i + (y'i)^2 + x + yi
* = x'^2 + 2x'y'i - y'^2 + x + yi ( i^2 := -1 )
* = x+x'^2-y'^2 + (y+2x'y')i
*
* := x'' + y'' i
*/
public void calculate(int sx, int sy) {
//
// Initialize Variables
//
size_x = sx;
size_y = sy;
prec_x = range_x / (double)size_x; // calculation steps ( precision_x )
prec_y = range_y / (double)size_y; // calculation steps ( precision_y )
if (keepProportions) {
if (prec_x > prec_y) {
prec_y = prec_x;
size_y = (int)(range_y / prec_y);
} else if (prec_y > prec_x) {
prec_x = prec_y;
size_x = (int)(range_x / prec_x);
}
}
size_x2 = size_x / 2;
size_y2 = size_y / 2;
//
// Initialize offscreen Image and Graphics
//
image = createImage( size_x, size_y );
graphics = image.getGraphics();
float br = (float)1.0;
if (super_hue == (float)0.0) {
br = (float)0.0;
}
// for percent info
int percent = 10;
double px0 = x0 - prec_x * (double)size_x2;
for ( int x = 0; x < size_x; x++ ) {
double py0 = y0 - prec_y * (double)size_y2;
for ( int y = 0; y < size_y ; y++ ) {
graphics.setColor(Color.getHSBColor(super_hue, (float)1.0, br));
double px = px0;
double py = py0;
for ( int col = 0; col < colors; col++ ) {
double px1 = px0+px*px-py*py;
py = py0+2*px*py;
px = px1;
if ( Math.abs(px) > 2.0 || Math.abs(py) > 2.0 ) {
float c = ((float)col / (float)colors)*hue_range + hue_offset;
if (c > 1.0f) {
c -= 1.0f;
}
graphics.setColor(Color.getHSBColor(c, (float)1.0,(float)1.0));
break;
}
}
graphics.drawLine(x,y,x,y);
py0 += prec_y;
}
px0 += prec_x;
if (mandelbrot != null) {
if (100*x/size_x >= percent) {
if (mandelbrot.runsAsApplet()) {
mandelbrot.getAppletContext().showStatus(percent+"%");
}
percent += 10;
}
}
}
areaSelected = false;
areaSelectionEnabled = true;
}
/**
* paint() for MandelbrotPanel
*/
public void paint(Graphics screen) {
// paint mandelbrot image
if ( image != null ) {
int sx = getSize().width; // get current width
int sy = getSize().height; // get current height
screen.drawImage( image, (sx-size_x)/2, (sy-size_y)/2, size_x, size_y, this );
}
}
/**
* destroy() for MandelbrotPanel graphics
*/
public void destroy() {
// dispose graphics
graphics.dispose();
}
/**
* MandelbrotMouseMotionListener
*/
private class MandelbrotMouseListener implements MouseListener {
public void mouseClicked( MouseEvent evt ) {
}
public void mouseEntered( MouseEvent evt ) {
}
public void mouseExited( MouseEvent evt ) {
}
public void mousePressed( MouseEvent evt ) {
}
public void mouseReleased( MouseEvent evt ) {
if ( areaSelected ) {
areaSelected = false;
areaSelectionEnabled = false;
// calculate parameters for new set from selected area
int mx = Math.min(selX0,selX1)+Math.abs(selX1-selX0)/2;
int my = Math.min(selY0,selY1)+Math.abs(selY1-selY0)/2;
x0 += (mx - size_x/2) * prec_x;
y0 += (my - size_y/2) * prec_y;
range_x *= (double)Math.abs(selX1-selX0)/(double)size_x;
range_y *= (double)Math.abs(selY1-selY0)/(double)size_y;
mandelbrot.stop();
mandelbrot.start();
}
}
}
/**
* MandelbrotMouseMotionListener
*/
private class MandelbrotMouseMotionListener implements MouseMotionListener {
private int realX1;
private int realY1;
public void mouseMoved(MouseEvent evt) {
}
public void mouseDragged(MouseEvent evt) {
if (areaSelectionEnabled) {
int sx = getSize().width; // get current width
int sy = getSize().height; // get current height
int x = evt.getX() - (sx-size_x)/2;
int y = evt.getY() - (sy-size_y)/2;
if ( !areaSelected ) {
areaSelected = true;
realX1 = selX1 = selX0 = x;
realY1 = selY1 = selY0 = y;
} else if ( x != selX1 || y != selY1 ) {
if (keepProportions) {
if (x!=realX1) {
// x-position has changed
realX1 = x;
realY1 = y;
// adjust y-value
if ( selY0 <= selY1 ) {
y = selY0 + Math.abs( x - selX0 ) * sy / sx;
if ( y < realY1 ) {
if ( ( selX0 <= x && x <= selX1 ) || ( selX0 >= x && x >= selX1 ) ) {
return;
}
}
} else {
y = selY0 - Math.abs( x - selX0 ) * sy / sx;
if ( y > realY1 ) {
if ( ( selX0 <= x && x <= selX1 ) || ( selX0 >= x && x >= selX1 ) ) {
return;
}
}
}
} else if (y!=realY1) {
// y-position has changed
realX1 = x;
realY1 = y;
// adjust x-value
if ( selX0 <= selX1) {
x = selX0 + Math.abs( y - selY0 ) * sx / sy;
if ( x < realX1 ) {
if ( ( selY0 <= y && y <= selY1 ) || ( selY0 >= y && y >= selY1 ) ) {
return;
}
}
} else {
x = selX0 - Math.abs( y - selY0 ) * sx / sy;
if ( x > realX1 ) {
if ( ( selY0 <= y && y <= selY1 ) || ( selY0 >= y && y >= selY1 ) ) {
return;
}
}
}
}
}
graphics.setXORMode(Color.white);
graphics.drawRect(Math.min(selX0,selX1),Math.min(selY0,selY1),Math.abs(selX1-selX0),Math.abs(selY1-selY0));
graphics.drawRect(Math.min(selX0,x),Math.min(selY0,y),Math.abs(x-selX0),Math.abs(y-selY0));
repaint();
selX1 = x;
selY1 = y;
}
}
}
}
}