分形理论(fractal theory)的奠基人曼德布罗特(Mandelbrot)发现了著名的Mandelbrot set,简称M集。M集可能是最复杂的数学对象,是分形、混沌领域的一种国际标志。
M集涉及到一些复数操作,包括复数乘法mul(Complex)、加法plus(Complex)和求模mod() (复数到坐标原点的距离)。由复数Complex类封装。
package API.graphics.fractal;
/**
* Complex.java.
* 提供M集所需要的复数功能,不完整的复数类。
* @author yqj2065
*/
public class Complex{
private final double x,y;//c = x + y *i即real + i* imaginary
/**
* 构造复数对象z = x +y *i
* @param x 实部/real
* @param y 虚部/imaginary
*/
Complex(double x,double y) {
this.x =x; this.y =y;
}
Complex(){ this(0,0); }
/**
*复数乘法。Complex multiplication
*
*/
public Complex mul(Complex a){
double newx =this.x*a.x-this.y*a.y;
double newy =2*this.x*a.y;
return new Complex(newx,newy);
}
public Complex plus(Complex a){
double newx=this.x +a.x;
double newy=this.y +a.y;
return new Complex(newx,newy);
}
/**
* 求模。该复数到坐标原点的距离
* @return |z| z = x + i*y
*/
public double mod(){
return (x!=0 || y!=0) ? Math.sqrt( x*x + y*y):0d;
}
}
M集定义:复平面上给定一个初始的复数Z0(通常令Z0为(0,0)点),对于任一复常数C,按照迭代公式:Zn+1 =Zn2+C
如果n趋向于无穷时,Zn+1有界,则称C属于Mandelbrot集。
书中提供了API.graphics.fractal.MSet。作为对《9.2.2 渲染引擎Graphics2D》的补充,添加了MandelbrotSet(在JFrame上绘制)和改进的MandelbrotSet2+DrawFrame。
1.JApplet中绘制
渲染(Rendering) /绘制,是在输出设备如屏幕或打印机上显示(display)几何形状、文本和图像的处理过程。
渲染引擎(Graphics和Graphics2D)完成渲染工作。
通常不必创建渲染引擎对象。
在组件/Component上面绘制图形时,
paint(Graphics)、update(Graphics)的形参就是渲染引擎,各种组件如Applet、JApplet、Frame在改写这些方法时有现成的渲染引擎可用;JComponent提供了方法paintComponent(Graphics)。
package API.graphics.fractal;
import java.awt.*;
import javax.swing.*;
public class MSet extends JApplet{
private int width=400,height =400;//复平面窗口分辨率width*height,即复平面的实际点数;
//研究表明,M集处于复平面的较小区间中,x取[-2.25 ,0.75],y取[-1.5,1.5]
private final double xMin=-2.25 , yMin=-1.5, xMax= 0.75 , yMax=1.5;
double xUnit= (xMax-xMin)/width; //绘图窗口中两个复数点的x间距
double yUnit= (yMax-yMin)/height;
int R = 10;//逃逸半径
int N = 100;//迭代次数。
@Override public void init(){
this.resize(width,height);
}
@Override public void paint(Graphics g){
//复平面上逐点绘制
for(int i=0;i<width;i++){
for( int j=0;j<height;j++){
double r = xMin+ i* xUnit;//real
double im = yMin+ j* yUnit;//imaginary
int color = 10*iterate(r,im);
if(color!=0) g.setColor(new Color(color));
else g.setColor(Color.yellow);//表示该数属于M集。
g.drawLine(i,j,i,j);
}
}//end for
}
/**
* 迭代.Z(n+1) = Z(n)*Z(n) + c
* 本来可以返回一个boolean值,为了设置绘制的颜色,返回迭代的次数。
*/
private int iterate(double real, double imaginary){
Complex c = new Complex(real,imaginary);//复常数C
Complex z = new Complex(0.0,0.0);//z0
for(int i =1; ; i++){
z =(z.mul(z)).plus(c);//z*z
if (z.mod() > R ) return i; //
if (i > N ) return 0;
}
}
}
2.JFrame中绘制
对于自定义的普通方法或main,可以使用java.awt.Component的方法public Graphics getGraphics()获得渲染引擎。但是通常使用BufferedImage进行绘制。
package API.graphics.fractal;
import java.awt.*;
import javax.swing.*;
import java.awt.image.BufferedImage;
public class MandelbrotSet{
private int width=400,height =400;//同上
int N = 100;//迭代次数。
public static void main(String[] args) {
MandelbrotSet m = new MandelbrotSet();
JFrame frame=new JFrame();
frame.setLayout(new FlowLayout());
BufferedImage image=new BufferedImage(m.width,m.height,BufferedImage.TYPE_INT_ARGB);
Graphics g=image.getGraphics();
for(int i=0;i<m.width;i++){
//同上
}
g.dispose();
JLabel label=new JLabel(new ImageIcon(image));
frame.add(label);
frame.setTitle("Graphics");
frame.setSize(m.width,m.height);
frame.setVisible(true);
}
private int iterate(double real, double imaginary){
//同上
}
}
3.工具的雏形
MandelbrotSet以及其他绘图程序,都将使用BufferedImage,而import javax.swing.*;不是MandelbrotSet希望使用的。
package API.graphics.fractal;
import java.awt.*;
import javax.swing.*;
import java.awt.image.BufferedImage;
/**
* 工具的雏形
*
* @author (yqj2065)
* @version (0.1)
*/
public class DrawFrame{
public static JFrame frame=new JFrame();
public static void setSize(int width,int height){
frame.setSize(width,height);
}
public static void draw(BufferedImage image){
frame.setLayout(new FlowLayout());
JLabel label=new JLabel(new ImageIcon(image));
frame.add(label);
frame.setTitle("Graphics");
frame.setVisible(true);
}
}
MandelbrotSet2对于MandelbrotSet的main方法进行了修改:
public static void main(String[] args) {
MandelbrotSet2 m = new MandelbrotSet2();
DrawFrame d =new DrawFrame();
BufferedImage image=new BufferedImage(m.width,m.height,BufferedImage.TYPE_INT_ARGB);
Graphics g=image.getGraphics();
for(int i=0;i<m.width;i++){
//
}
g.dispose();
d.setSize(m.width,m.height);
d.draw(image);
}