您的位置:首页 > 编程语言 > Java开发

详解如何用java实现Koch雪花的绘制

2013-11-24 13:34 1211 查看
详解如何用java实现Koch雪花的绘制
 

1,前言

 

    本周计算机图形学老师布置了编程作业:用VC++实现Koch雪花的绘制,课本配套的实验指导书上有完整的源代码,照敲就行了,没什么意思,自己于是就有了用这学期学的Java语言来实现一下的想法。

 

2,问题描述

 

    Koch雪花其实就是一种Koch曲线。
    Koch曲线是一个数学曲线,同时也是早期被描述的一种分形曲线。它由瑞典数学家Helge
von Koch在1904年发表的一篇题为“从初等几何构造的一条没有切线的连续曲线”的论文中提出。有一种Koch曲线是象雪花一样,被称为Koch雪花(或Koch星),它是由三条Koch曲线围成的等边三角形。至于更详细的请读者百度百科。
    如图所示:





3,解决方案

 

    设想从一个线段开始,根据下列规则构造一个Koch曲线:

    1.三等分一条线段;
    2.用一个等边三角形替代第一步划分三等分的中间部分;
    3.在每一条直线上,重复第二步。
    Koch曲线是以上步骤地无限重复的极限结果。
   
Koch曲线的长度为无穷大,因为以上的变换都是一条线段变四条线段,每一条线段的长度是上一级的1/3,因此操作n步的总长度是(4/3)n:若n→∞,则总长度趋于无穷。Koch曲线的分形维数是log
4/log 3 ≈ 1.26,其维数大于线的维数(1),小于Peano填充曲线的维数(2)。
    Koch曲线是连续的,但是处处不可导的。
    Koch雪花的面积是 2* √3 * s²/5 ,这里的s是最初三角形的边长,Koch雪花的面积是原三角形面积的8/5,它成为一条无限长的边界围绕着一个有限的面积的几何对象。
 

    下面先看自己画的一幅图(生成元初始状态):

 



 

    图中可以看到生成元的起点坐标为(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次变化

。。。。。。

 

4,具体代码

 

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);
}
}


 

 

5,运行结果截图

 



 



 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: