Java Chaos Game 噪声游戏两则
Java Chaos Game噪声游戏两则
[简介]
最近一直在读《深奥的简洁》,里面有一章介绍了几种使用噪声产生分形图的方法,感觉很有意思,于是尝试使用计算机模拟了一下,效果还不错(噪声法比传统迭代法在编程上好实现一些,后来发现这类算法还不少,搜索chaos game可以找到更多)。
本篇程序源文件及其依赖jar包已经打包,可以到这里GitHub下载。
[Sierpinski三角形的噪声产生法]
在这些噪声游戏中,Sierpinski(谢尔宾斯基)三角形的生成规则可谓是最简单的:
1.在平面上选取三个点,标记为1、2、3,作为大三角形的顶点。
2.选择其中一点,作为“当前点”(比如选择1号)。
3.产生1~3的随机数,在该数表达的顶点与“当前点”的中点绘制一个新点,并将新点作为“当前点”。
4.重复步骤3,即可逼近图案。
*.注意 随机数最好不要使用以时间作为种子的产生方式。
[模拟程序]
package com.geiv.chaos; import java.awt.event.KeyEvent; import com.thrblock.util.RandomSet; import geivcore.DefaultFactor; import geivcore.KeyFactor; import geivcore.KeyListener; import geivcore.R; import geivcore.UESI; import geivcore.enginedata.obj.Obj; public class Sierpinski extends DefaultFactor implements KeyListener{ UESI UES; Obj[] basePoint; Obj crtPoint; public Sierpinski(UESI UES,int times){ this.UES = UES; basePoint = new Obj[3];//创建三个基准点 for(int i = 0;i < 3;i++){ basePoint[i] = UES.creatObj(UESI.BGIndex); basePoint[i].addGLPoint("70DBDB",0,0); basePoint[i].show(); } basePoint[0].setCentralX(400);//设置三点位置 basePoint[0].setCentralY(60); basePoint[1].setCentralX(60); basePoint[1].setCentralY(550); basePoint[2].setCentralX(740); basePoint[2].setCentralY(550); crtPoint = basePoint[0];//将0号点作为当前点 this.setKeyListener(this); UES.pushKeyBoardIO(this); for(int i = 0;i < times;i++){ generateNew(); } } @Override public void doKeyBord(KeyFactor whom, int keyCode, boolean ispressed) {//挂载回调 if(ispressed){ if(keyCode == KeyEvent.VK_SPACE){//空格对应创建一个新点 generateNew(); }else if(keyCode == KeyEvent.VK_A){//A对应创建100个新点 for(int i = 0;i < 100;i++){ generateNew(); } }else if(keyCode == KeyEvent.VK_B){//B对应创建1000个新点 for(int i = 0;i < 1000;i++){ generateNew(); } } } } public void generateNew(){ Obj flagPoint = basePoint[RandomSet.getRandomNum(0, 2)];//随机选择基准点之一 float nx = (flagPoint.getCentralX() + crtPoint.getCentralX())/2f;//计算中点 float ny = (flagPoint.getCentralY() + crtPoint.getCentralY())/2f; Obj newPoint = UES.creatObj(UESI.BGIndex);//创建新点 newPoint.addGLPoint("70DBDB",0,0); newPoint.setColor(RandomSet.getRandomColdColor()); newPoint.setCentralX(nx);//设置坐标 newPoint.setCentralY(ny); newPoint.show(); crtPoint = newPoint;//置为当前点 } public static void main(String[] args) { UESI ues = new R(); new Sierpinski(ues,0);//后面的构造参数可以设置初始点数。 } }
[模拟结果]
在B键按下时
[Barnsleyfern的噪声产生法]
相比于Sierpinski三角的简单规则性,Barnsleyfern(分形羊齿草)给人以更加复杂的印象,出于它的复杂性,混沌学科经常拿出它来证明“简单规则也可产生复杂对象”的结论。
它的产生规则也不是很复杂:
1.首先给定”当前点”(0,0),我们用ox,oy表示横纵坐标。
2.计算下一点(nx,ny)需要以一定随机规则选择下列四种迭代公式之一:
1)以%1的概率选择此迭代公式:
nx = 0;
ny = 0.16f * oy;
2)以%85的概率选择此迭代公式:
nx = 0.85*ox+ 0.04*oy;
ny = -0.04*ox + 0.85*oy + 1.6;
3)以%7的概率选择此迭代公式:
nx = 0.2*ox- 0.26*oy;
ny = 0.23*ox+ 0.22*oy + 1.6;
4)以%7的概率选择此迭代公式:
nx = -0.15*ox + 0.28*oy;
ny = 0.26*ox + 0.24*oy + 0.44;
3.绘制(nx,ny),并将其设为当前点,重复2,即可无限逼近结果。
↑以上公式摘自Wiki:http://en.wikipedia.org/wiki/Barnsley_fern。在编程时,我发现一个问题,Wiki并未指明这个坐标的决对值与屏幕大小的关系,也并未说明x、y轴的方向,在我自己定义的坐标系下绘制总是不成功,后来我按照公式搜索,找到了这个面:http://people.sc.fsu.edu/~jburkardt/cpp_src/fern_opengl/fern.cpp。这是一个C++下的OPENGL程序,而里面用了与Wiki相同的公式,也就是说,这组公式是以Opengl的坐标系为基准的,在做了对应变换后终于成功绘制。
[模拟程序]
package com.geiv.chaos; import geivcore.DefaultFactor; import geivcore.KeyFactor; import geivcore.KeyListener; import geivcore.R; import geivcore.UESI; import geivcore.enginedata.obj.Obj; import java.awt.Color; import java.awt.event.KeyEvent; import com.thrblock.util.RandomSet; public class Barnsleyfern extends DefaultFactor implements KeyListener{ UESI UES; Obj crtPoint; public Barnsleyfern(UESI UES,int times){ this.UES = UES; crtPoint = UES.creatObj(UESI.BGIndex); crtPoint.addGLPoint("70DBDB",0,0); crtPoint.show(); crtPoint.setCentralX(0); crtPoint.setCentralY(0); UES.setViewOffsetX(90); this.setKeyListener(this); UES.pushKeyBoardIO(this); for(int i = 0;i < times;i++){ generateNew(); } } @Override public void doKeyBord(KeyFactor whom, int keyCode, boolean ispressed) {//键盘IO的方式同上例 if(ispressed){ if(keyCode == KeyEvent.VK_SPACE){ generateNew(); }else if(keyCode == KeyEvent.VK_A){ for(int i = 0;i < 100;i++){ generateNew(); } }else if(keyCode == KeyEvent.VK_B){ for(int i = 0;i < 1000;i++){ generateNew(); } } } } public void generateNew(){ float nx,ny; float ox = crtPoint.getCentralX()/150f,oy = (600 - crtPoint.getCentralY())/60f;//这里做了OPENGL坐标转换,在设置新点位置时对应反转。 double code = 100.0 * RandomSet.getRandomFloatIn_1();//随机浮点数数0~100 if(code >= 0&&code <= 1){ nx = 0; ny = 0.00f * ox + 0.16f * oy; } else if(code > 1&& code <= 86){ nx = 0.85f*ox + 0.04f*oy; ny = -0.04f*ox + 0.85f*oy + 1.6f; } else if(code > 86&& code <= 93){ nx = 0.2f*ox - 0.26f*oy; ny = 0.23f*ox + 0.22f*oy + 1.6f; } else{ nx = -0.15f*ox + 0.28f*oy; ny = 0.26f*ox + 0.24f*oy + 0.44f; } Obj newPoint = UES.creatObj(UESI.BGIndex); newPoint.addGLPoint("70DBDB",0,0); newPoint.setColor(Color.GREEN); newPoint.setCentralX(nx*150f);//将之前的坐标变换抵消 newPoint.setCentralY(600 - ny*60f); newPoint.show(); crtPoint = newPoint;//设置新点为当前点。 } public static void main(String[] args) { UESI ues = new R(); new Barnsleyfern(ues,0); } }
[模拟结果]
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。