基于“L-system”,实现分形树。-JS

L-system

Lindenmayer 系统简称L系统,是1968年由匈牙利生物学家林登麦伊尔(Lindenmayer)提出的有关生长发展中的细胞交互作用的数学模型,尤其被广泛应用于植物生长过程的研究。

原理

L-system或被称为Lindenmayer system是一个相似重写系统,是一系列不同形式的正规语法规则,多被用于植物生长过程建模,但是也被用于模拟各种生物体的形态。L-system也能用于生成自相似的分形,例如迭代函数系统。带有参数的L-system包括V:变量符号集合,S:常量符号集合,W:初始状态串,P:产生式规则。

应用于分形树

对于本程序实现的分形树,所用到的变量是“F”,“+”,“-”,“[”,“]”。
其中“F”代表按当前角度向前画线,“+”表示顺时针旋转一个角度θ,“-”表示逆时针旋转一个角度θ,“[”表示当前位置的坐标以及角度入栈保存起来,为下一次回到当前结点绘制树的分支提供起始条件,“]”则表示出栈,当前栈顶元素被取出,用来绘制当前直线。

HTML与JS代码实现

在本次程序设计中,主要是利用canvas画布来绘制分形树。
变量的符号的集合为:“F”,“+”,“-”,“[”,“]”。
其中“F”代表按当前角度向前画线,“+”表示顺时针旋转一个角度θ,“-”表示逆时针旋转一个角度θ,“[”表示当前位置的坐标以及角度入栈保存起来,为下一次回到当前结点绘制树的分支提供起始条件,“]”则表示出栈,当前栈顶元素被取出,用来绘制当前直线。
这里的栈顶元素可以理解成一个结构体元素,这个元素包含当前的分支的坐标以及分支的方向。

已知公理利用规则得出下一个公理

//确定下一个公理
		function generate() {
			var nextAxiom = "";
			r *= γ;
			//遍历每一个元公理,进行变换
			for (var i = 0; i < Axiom.length; i++) {
				var current = Axiom.charAt(i);
				var found = false;
				for (var j = 0; j < rules.length; j++) {
					//每个公理只能与规则匹配一次
					if (current == rules[j].a) {
						found = true;
						nextAxiom += rules[j].b;
						break;
					}
				}
				if (!found)
					nextAxiom += current;
			}
			//重置下一个起始点与起始角度
			Axiom = nextAxiom;
			nowx = window.innerWidth / 3, nowy = -50;
			nowangel = Math.PI / 2;
		}

分形树的绘制

function drawTree() {
			//遍历公理
			for (var i = 0; i < Axiom.length; i++) {
				current = Axiom.charAt(i);
				if (current == "F") {
					forward();
				}
				//right
				else if (current == "+") {
					nowangel -= angel;
				}
				//left
				else if (current == "-") {
					nowangel += angel;
				} else if (current == "[") {
					var s = new Array;
					s["x"] = nowx;
					s["y"] = nowy;
					s["a"] = nowangel;
					lstack.push(s);
				} else if (current == "]") {
					var s = lstack.pop();
					nowx = s.element["x"];
					nowy = s.element["y"];
					nowangel = s.element["a"];
				}
			}
		}
		//向前画线
		function forward() {
			//镜像翻转至canvas坐标系
			context.beginPath();
			context.moveTo(nowx, window.innerHeight - 100 - nowy);
			//计算下一步的位置
			var x = nowx + r * Math.cos(nowangel);
			var y = nowy + r * Math.sin(nowangel);
				
			context.lineTo(x, window.innerHeight - 100 - y);

			context.stroke();
			nowx = x;
			nowy = y;
		}

GUI界面的实现

var controls = new function() {
				this.grow = function() {
					context.strokeStyle = this.color
					generate();
					drawTree();
				}
				this.clear = function() {
					reset();
				}
				this.color="#FF0000";
			}
			var gui = new dat.GUI();
			gui.domElement.style.position = 'absolute';
			gui.domElement.style.width = '400px';
			gui.domElement.style.height= '400px';

			gui.add(controls, 'grow');
			gui.add(controls, 'clear');
			gui.addColor(controls, 'color');

二阶分形树的讲解

我们知道一开始的公理为‘F’,它表示的含义就是向前绘制一定的距离。而利用规则产生下一个公理,就是说要把前一个公理中的‘F’替换成‘F F + [ + F – F – F ] - [ - F + F + F ]’,所以我们二阶分形树的公理就变成了‘F F + [ + F – F – F ] - [ - F + F + F ]’,同理,如果我们要绘制三阶分形树,我们就要把上一次的公理中的’F’进行替换。可以写出三阶分形树的公理为‘FF+[+F-F-F]-[-F+F+F]FF+[+F-F-F]-[-F+F+F]+[+FF+[+F-F-F]-[-F+F+F]-FF+[+F-F-F]-[-F+F+F]-FF+[+F-F-F]-[-F+F+F]]-[-FF+[+F-F-F]-[-F+F+F]+FF+[+F-F-F]-[-F+F+F]+FF+[+F-F-F]-[-F+F+F]]’,可以看出树的阶数越高,公理就越复杂,在这里我们暂且讨论二阶分形树。

当前公理:‘F F + [ + F – F – F ] - [ - F + F + F ]’。
利用“其中“F”代表按当前角度向前画线,“+”表示顺时针旋转一个角度θ,“-”表示逆时针旋转一个角度θ,“[”表示当前位置的坐标以及角度入栈保存起来,为下一次回到当前结点绘制树的分支提供起始条件,“]”则表示出栈,当前栈顶元素被取出,用来绘制当前直线。”

这里我们旋转的角度是20°,向前移动的距离是280个距离。这里需要注意的是,为了让树看起来更逼真,当我们的阶数升高时,树枝应该变短

我们绘制的二阶分形树应当如下,二阶分形树可以自己用笔绘制一下,看看是不是这样,但是三阶和三阶以上的分形树就不建议手画了,因为公理非常的长,非常麻烦。
在这里插入图片描述

利用GUI界面绘制多阶的分形树

通过制作一个简易的GUI界面,实验多阶分形树的绘制,在此程序中,只需鼠标点击“grow”按键,树将会变成下一阶,而且还可以改变树的颜色等等

三阶

在这里插入图片描述

四阶

在这里插入图片描述

五阶

在这里插入图片描述

六阶

在这里插入图片描述
在这里插入图片描述

总结

L-system广泛应用与分形的程序设计中,本程序利用了L-system实现分形树,之后将利用L-system绘制koch曲线,比较常规的递归绘制与L-system实现的绘制,显然当绘制的阶数越来越高时,递归的深度也会越来越高,会出现栈溢出的现象,而L-system却不会出现这个问题,L-system只需要存储字符串,也就是我们在本程序中提到的公理。计算机存储字符串还是非常给力的,不可能几个字符串就把内存占满了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值