详解如何用java实现Koch雪花的绘制
2013-11-24 13:34
1211 查看
详解如何用java实现Koch雪花的绘制
本周计算机图形学老师布置了编程作业:用VC++实现Koch雪花的绘制,课本配套的实验指导书上有完整的源代码,照敲就行了,没什么意思,自己于是就有了用这学期学的Java语言来实现一下的想法。
Koch雪花其实就是一种Koch曲线。
Koch曲线是一个数学曲线,同时也是早期被描述的一种分形曲线。它由瑞典数学家Helge
von Koch在1904年发表的一篇题为“从初等几何构造的一条没有切线的连续曲线”的论文中提出。有一种Koch曲线是象雪花一样,被称为Koch雪花(或Koch星),它是由三条Koch曲线围成的等边三角形。至于更详细的请读者百度百科。
如图所示:
设想从一个线段开始,根据下列规则构造一个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次变化
。。。。。。
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,运行结果截图
相关文章推荐
- java结合keytool如何实现非对称加密与解密详解
- 200行Java代码如何实现依赖注入框架详解
- 详解Java如何实现图像灰度化
- 详解Java如何实现基于Redis的分布式锁
- 详解Kotlin中如何实现类似Java或C#中的静态方法
- Java关于数据结构及如何实现:表、栈与队列详解
- java结合keytool如何实现非对称签名和验证详解
- Java利用happen-before规则如何实现共享变量的同步操作详解
- JAVA中Swing的绘制原理以及如何实现动态图画
- 深入理解Java的整型类型:如何实现2+2=5?
- 什么是java序列化,如何实现java序列化?
- 如何更好的使用Java8中方法引用详解
- 如何使用 Java8 实现观察者模式
- 详解Java实现缓存(LRU,FIFO)
- 分别用Java应用程序和Applet程序实现星星三角形图案的绘制
- 如何实现两个字符串的交叉合并功能java实现
- Visual Paradigm - 一个用于绘制UML的java实现的一个工具软件
- Java利用endorsed如何覆盖jdk提供的类详解
- 什么是java序列化,如何实现java序列化?
- Java反射机制详解(3) -java的反射和代理实现IOC模式 模拟spring