Árvore por L-system

Uma string reescrita quatro vezes por duas regras e depois lida como movimentos de tartaruga, um Line2D por galho.

O editor acima está rodando este projeto. Mude uma linha e ele recarrega.

Classes do Godot usadas

Line2DLabel

O código

scripts/main.gd
extends Node

const AXIOM: String = 'X'
const RULE_X: String = 'F+[[X]-X]-F[-FX]+X'
const RULE_F: String = 'FF'
const DEPTH: int = 4
const ANGLE: float = 24.0
const LENGTH: float = 16.0
const PER_FRAME: int = 5

var branches: Array[Line2D] = []
var grown: int = 0


func _ready() -> void:
	_add_background()

	var view: Vector2 = get_viewport().get_visible_rect().size

	_grow(_expand(), Vector2(view.x * 0.5, view.y - 40.0))

	for branch in branches:
		branch.visible = false

	_add_label('axiom %s, depth %d, angle %d degrees' % [AXIOM, DEPTH, int(ANGLE)], Vector2(40, 36))
	_add_label('X -> %s' % RULE_X, Vector2(40, 66))
	_add_label('F -> %s' % RULE_F, Vector2(40, 96))

	print('the string is rewritten %d times and then read as turtle moves' % DEPTH)


func _process(_delta: float) -> void:
	var target: int = mini(grown + PER_FRAME, branches.size())

	while grown < target:
		branches[grown].visible = true
		grown += 1


func _expand() -> String:
	var text: String = AXIOM

	for i in DEPTH:
		var next: String = ''

		for symbol in text:
			next += _rule(symbol)

		text = next

	return text


func _rule(symbol: String) -> String:
	match symbol:
		'X':
			return RULE_X
		'F':
			return RULE_F

	return symbol


func _grow(text: String, start: Vector2) -> void:
	var at: Vector2 = start
	var heading: Vector2 = Vector2.UP
	var depth: int = 0
	var stack: Array = []

	for symbol in text:
		match symbol:
			'F':
				var to: Vector2 = at + heading * LENGTH * pow(0.88, depth)
				_add_branch(at, to, depth)
				at = to
			'+':
				heading = heading.rotated(deg_to_rad(ANGLE))
			'-':
				heading = heading.rotated(-deg_to_rad(ANGLE))
			'[':
				stack.push_back({at = at, heading = heading, depth = depth})
				depth += 1
			']':
				var saved: Dictionary = stack.pop_back()
				at = saved.at
				heading = saved.heading
				depth = saved.depth


func _add_branch(from: Vector2, to: Vector2, depth: int) -> void:
	var weight: float = clampf(float(depth) / 6.0, 0.0, 1.0)

	var branch: Line2D = Line2D.new()
	branch.points = PackedVector2Array([from, to])
	branch.width = lerpf(7.0, 1.4, weight)
	branch.default_color = Color('#8da5f3').lerp(Color('#8eef97'), weight)
	add_child(branch)
	branches.append(branch)


func _add_label(text: String, at: Vector2) -> void:
	var label: Label = Label.new()
	label.text = text
	label.position = at
	label.add_theme_color_override('font_color', Color('#999999'))
	add_child(label)


func _add_background() -> void:
	var background: ColorRect = ColorRect.new()
	background.color = Color('#12141a')
	add_child(background)
	background.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)

Mais exemplos de Procedural