栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Python

第一章 专栏介绍及简单分形树的代码实现

Python 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

第一章 专栏介绍及简单分形树的代码实现

目录

一、专栏简介

二、专栏成果展示

2.1 简单分形树

2.2 一般分形树

2.3 复杂分形树

 三、简单分形树的生成原理

3.1 生成步骤

3. 2 涉及到的数学方法

四、简单分形树的代码实现


一、专栏简介

专栏系列文章将由浅入深地介绍分形树的生成原理,揭示蕴含其中的数学方法,并使用Java语言实现绘制这类分形的算法。欢迎对分形、数学及编程感兴趣的伙伴一同探讨研究!!!


二、专栏成果展示

2.1 简单分形树

使用固定不变的参数可以快捷地绘制出如下图所示的分形树。这类分形树的特点是缺乏变化,与自然状态下的树木差别很大,它的意义更多地在于提供了一个得到其他复杂分形树的“初代机”。

2.2 一般分形树

对上面简单分形树的生长参数作随机扰动,以丰富形态变化,模拟出相对自然的状态,可避免显地呆板无趣。绘制效果如下图所示。

2.3 复杂分形树

借助Turtle指令,记录下绘制分形树时Turtle经过每个像素的次数,并以此作为着色的依据。如果再配合着渐变色进行绘制,就可以得到一些非常有趣的图片。绘制效果如下图所示。


 三、简单分形树的生成原理

3.1 生成步骤

专栏第一章将具体分析简单分形树,下图演示了此类分形树的生成步骤:按照相同的方式不断地在末梢处长出新的分支(递归),当达到一定次数后,结束生长。

因为生长规律是不变的,所以只需分析任意某段树枝是如何长出分支的,即可套用到其他树枝上。结合下图,容易分析得到单根树枝长出分支的方式步骤:

步骤一:主干向左逆时针旋转α角度,然后按某个系数进行缩放;

步骤二:主干起点平移到自身的终点上,成为左侧分支;

步骤三:生成右侧分支的方式步骤与左侧的类似,二者唯一的区别在于旋转方向相反。

3. 2 涉及到的数学方法

a. 主干与分支之间存在角度关系,用简单的三角函数进行处理即可;

b. 使用向量(Vector)来理解物体的旋转和缩放。如下图所示,向量运算有助于对空间变换问题形成本质上的认识,反之,一开始就着眼于坐标计算则容易使人一头雾水,往往事倍功半。


四、简单分形树的代码实现

了解了关于一般分形树的基本原理后,便可开始定义相关类。

首先,定义一个简单分形树类SimpleTree,并在构造函数中完成初始化操作,其属性包括分支相对于主干的转动角度、画笔和窗口高度等:

	// 分支相对于主干的旋转角度及其正/余弦值
	// 这三个属性不要求很高的数值精度,数据类型使用float即可
	// 这类分形树设定左右分支相对于主干对称分布,因此只需要一个角度属性即可。有需要的话,可以定义左右两个角度值,方法大同小异。
	private float angle, cosAngle, sinAngle;

	// 用来绘制分形树的画笔。在哪个窗口绘制,就在哪个窗口获取画笔
	private Graphics2D g2d;

	// 窗口高度,用于坐标转换
	private int boardHeight;

	public SimpleTree(Graphics g, int boardHeight) {

		// 将默认角度值18°转换为弧度制,再进行三角函数计算
		angle = (float) (18 * Math.PI / 180);
		cosAngle = (float) Math.cos(angle);
		sinAngle = (float) Math.sin(angle);

		// 初始化画笔,设置画笔颜色和宽度
		g2d = (Graphics2D) g;
		g.setColor(new Color(255, 120, 0));
		g2d.setStroke(new BasicStroke(4.5f));

		this.boardHeight = boardHeight;
	}

定义绘制分形树的方法:

	
	public void drawTree(float x0, float y0, double vx, double vy) {

		// 主干的长度
		float branchLen = (float) Math.sqrt(vx * vx + vy * vy);

		// 长度小于12,不再生长(递归结束条件,数值可根据需要更改)
		if (branchLen < 12) {
			return;
		}

		// P(x1,y1):主干的终点坐标,同时也是它的两个分支的起点坐标
		float x1 = (float) (x0 + vx);
		float y1 = (float) (y0 + vy);

		// 绘制出主干
		// Java绘制窗口在高度上以从上往下为正方向,这与我们的观察习惯相反,为方便观察,需要在这个方向上进行一个坐标变换
		g2d.drawLine((int) x0, boardHeight - (int) y0, (int) x1, boardHeight - (int) y1);

		// 计算左侧分支向量V(leftVx, leftVy)
		// 第一步:主干向量向左旋转angle角度
		double leftVx = vx * cosAngle - vy * sinAngle;
		double leftVy = vx * sinAngle + vy * cosAngle;
		// 第二步:旋转之后再进行缩放,得到左侧分支向量。每生长一次,树枝长度减11(数值可根据需要更改)
		// 计算缩放系数
		float ratio = (branchLen - 11) / branchLen;
		leftVx *= ratio;
		leftVy *= ratio;

		// 递归调用drawTree方法,只不过这次绘制的是左侧分支
		drawTree(x1, y1, leftVx, leftVy);

		// 计算右侧分支向量V(rightVx, rightVy)
		// 注意旋转时以逆时针方向为正,向右旋转为顺时针方向,所以旋转角度应为-angle。cos(-angle)=cos(angle),sin(-angle)=-sin(angle)
		double rightVx = vx * cosAngle + vy * sinAngle;
		double rightVy = -vx * sinAngle + vy * cosAngle;
		rightVx *= ratio;
		rightVy *= ratio;

		// 递归调用drawTree方法,绘制右侧分支
		drawTree(x1, y1, rightVx, rightVy);
	}

SimpleTree类定义完成后,需要一个绘图窗口,通过调用SimpleTree对象的drawTree方法,查看绘制效果:

public class Entrance extends JFrame {

	private static final long serialVersionUID = 1L;

	public static void main(String[] args) {
		Entrance ent = new Entrance();
		ent.initUI();
	}

	private void initUI() {

		// 窗口的一些必要设置
		this.setTitle("FractalTree");
		this.getContentPane().setBackground(new Color(245, 251, 255));
		this.setSize(1350, 900);
		this.setLocationRelativeTo(null);
		this.setResizable(false);
		this.setVisible(true);

		// 画笔
		Graphics g = this.getGraphics();

		// new一个SimpleTree类对象,传参
		SimpleTree tree = new SimpleTree(g, 900);

		this.addMouseListener(new MouseAdapter() {
			public void mouseClicked(MouseEvent e) {
				// 点击窗口即开始绘制分形树
				tree.drawTree(675, 10, 0, 120);
			}
		});
	}
}

绘制效果如下图

如前所述,简单分形树缺乏变化,因此可能需要考虑如何丰富绘制效果,例如,下图通过重复绘制的方法来增加层次感。

 参考代码:

		this.addMouseListener(new MouseAdapter() {

			public void mouseClicked(MouseEvent e) {

				// 用于产生随机颜色
				Random rand = new Random();

				// 初始方向角105°
				float angle = (float) (105 * Math.PI / 180);

				for (int i = 0; i < 6; i++) {

					// V(vx,vy)
					double vx = 120 * Math.cos(angle);
					double vy = 120 * Math.sin(angle);

					// 需要在Simple类中定义setColor和setStroke方法,较为简单,不再赘述
					// 随机分配分形树的颜色,宽度则按照循环次数递减
					tree.setColor(new Color(rand.nextInt(256), rand.nextInt(256), rand.nextInt(256)));
					tree.setStroke(5.5f - i * 0.8f);
					tree.drawTree(675, 10, vx, vy);

					// 每画完一棵树,初始方向角减5°,循环画一下棵树
					angle -= 5.0 * Math.PI / 180;
				}
			}
		});

丰富视效的方法还有很多,不一一列举了,感兴趣的读者可以发挥自己的脑洞,做出自己想象中的效果。本章内容至此结束,专栏第一篇,非常期待大家的建议与反馈。下一篇将详细介绍一般分形树,干货满满,敬请期待!!!

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/983945.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号