详解如何用java实现Koch雪花的绘制
本周计算机图形学老师布置了编程作业:用VC++实现Koch雪花的绘制,课本配套的实验指导书上有完整的源代码,照敲就行了,没什么意思,自己于是就有了用这学期学的Java语言来实现一下的想法。
设想从一个线段开始,根据下列规则构造一个Koch曲线:
下面先看自己画的一幅图(生成元初始状态):
图中可以看到生成元的起点坐标为(x1,y1),终点坐标为(x2,y2),线段长度记为length,其递归深度depth = 0;
再看自己画的另一幅图(生成元第一次变化):
把变化后的每段线段长度记为len。图上的坐标可以这样计算:
(xa,ya)
xa = x1 + (x2-x1)/3.0;
ya = y1 + (y2-y1)/3.0;
(xb,yb)
xb = (xa+x2)/2.0;
yb = (ya+y2)/2.0;
(xc,yc)
xc = xa + len*cosα;
yc = ya - len*sinα;
对于len
len = length/(3^depth); // 此次depth = 0;
至于角度α的求解,可以先算出直线本身的倾斜角度actan((y2-y1)/(x2-x1)),再加上初始角度α0,则有α = actan((y2-y1)/(x2-x1)) + α0;
生成元第二次~第n次变化
。。。。。。
package KochSnowFlake;
import java.awt.*;
import javax.swing.*;
/**
* @Title Koch雪花
* @Author 孙琨
* @Date 2013-11-23
* @At XUST
* @All Copyright by 孙琨
*
*/
public class FractalJPanel extends JPanel { // Koch雪花分形具体算法
private int depth; // 递归深度
private Color color;
private final static int WIDTH = 600;
private final static int HEIGHT = 700;
private final static int length = 450;
public FractalJPanel(int depth) {
setColor(Color.BLUE);
setDepth(depth);
setBackground(Color.WHITE);
setPreferredSize(new Dimension(WIDTH, HEIGHT));
}
private double getAngle(int x1, int y1, int x2, int y2) { // 计算相邻两直线间的夹角
return Math.atan2((y1-y2), (x2-x1)) + Math.PI/3;
}
public void drawFractal(int depth, int x1, int y1, int x2, int y2, Graphics g) { // 递归绘制
// 递归结束条件
if(depth==0) {
g.drawLine(x1, y1, x2, y2);
} else {
// 计算坐标(xa,ya)
int xa = (int)Math.round(x1 + (x2-x1)/3.0);
int ya = (int)Math.round(y1 + (y2-y1)/3.0);
// 计算坐标(xb,yb)
int xb = (int)Math.round((xa + x2) / 2.0);
int yb = (int)Math.round((ya + y2) / 2.0);
// 计算递归后的线元长度
int len = (int)(length/Math.pow(3, (this.depth-depth+1)));
double angle = getAngle(x1, y1, x2, y2);
// 计算坐标(xc,yc)
int xc = (int)Math.round(xa + len * Math.cos(angle));
int yc = (int)Math.round(ya - len * Math.sin(angle));
// 递归
drawFractal(depth-1, x1, y1, xa, ya, g);
drawFractal(depth-1, xa, ya, xc, yc, g);
drawFractal(depth-1, xc, yc, xb, yb, g);
drawFractal(depth-1, xb, yb, x2, y2, g);
}
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
// 绘制最初的三条直线,形成初始三角形图案
g.setColor(color);
drawFractal(depth, 50, 450, 275, 60, g);
drawFractal(depth, 275, 60, 500, 450, g);
drawFractal(depth, 500, 450, 50, 450, g);
}
// 获取递归深度
public int getDepth() {
return depth;
}
// 设置递归深度
public void setDepth(int depth) {
this.depth = depth;
}
// 设置颜色
public void setColor(Color color) {
this.color = color;
}
}
package KochSnowFlake;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
/**
* @Title Koch雪花
* @Author 孙琨
* @Date 2013-11-23
* @At XUST
* @All Copyright by 孙琨
*
*/
public class FractalJFrame extends JFrame { // Koch 雪花整体设计
private final static int WIDTH = 600;
private final static int HEIGHT = 700;
private final static int MIN_DEPTH = 0; // 最小递归深度
private Color color = Color.BLUE;
private JButton increaseDepthJButton; // 增加递归深度按钮
private JButton decreaseDepthJButton; // 减少递归深度按钮
private JButton setColorJButton; // 设置颜色按钮
private JPanel mainJPanel;
private JPanel controlJPanel;
private FractalJPanel drawSpace; // Koch雪花分形具体算法类的一个对象
private JLabel depthJLabel; // 记录递归深度的标签
public FractalJFrame() {
// 添加标题
super("Koch雪花");
// 添加按钮
controlJPanel = new JPanel();
setColorJButton = new JButton("设置颜色");
increaseDepthJButton = new JButton("增加递归深度");
decreaseDepthJButton = new JButton("减少递归深度");
controlJPanel.add(setColorJButton);
controlJPanel.add(increaseDepthJButton);
controlJPanel.add(decreaseDepthJButton);
// 设置按钮相应的有关监听器
setColorJButton.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent event) {
color = JColorChooser.showDialog(
FractalJFrame.this, "请选择一种颜色", color);
if(color == null) {
color = Color.BLUE;
}
drawSpace.setColor(color);
}
}
);
increaseDepthJButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
int depth = drawSpace.getDepth();
depth++;
if(depth >= MIN_DEPTH) {
depthJLabel.setText("depth: " + depth);
drawSpace.setDepth(depth);
repaint();
}
}
}
);
decreaseDepthJButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
int depth = drawSpace.getDepth();
depth--;
if(depth >= MIN_DEPTH) {
depthJLabel.setText("depth: " + depth);
drawSpace.setDepth(depth);
repaint();
}
}
});
depthJLabel = new JLabel("depth: 0");
controlJPanel.add(depthJLabel);
drawSpace = new FractalJPanel(0);
mainJPanel = new JPanel();
mainJPanel.setLayout(new BorderLayout());
mainJPanel.add(controlJPanel, BorderLayout.NORTH);
mainJPanel.add(drawSpace);
add(mainJPanel);
setSize(WIDTH, HEIGHT);
setVisible(true);
}
public static void main(String args[]) { // 主方法
FractalJFrame frame = new FractalJFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}