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

详解如何用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,运行结果截图

 

 

 

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值