L系统森林中的重叠树木
我用Python的海龟图形库写了一个程序,模拟森林中的树木生长。程序里有三种树的样式,它们是随机选择的,树木的起始位置和角度也是随机的。我选了一些看起来很酷的树的样式,但我遇到的问题是,很多树木重叠在一起,所以看起来更像是一个五岁小孩的涂鸦,而不是一片森林。
有没有办法让这种重叠的情况 少一些?当你看一片森林时,确实有些树和树叶会重叠,但绝对不会像这样:
因为程序里有很多随机因素,我不太确定该怎么处理这个问题。
这是我的代码:
import turtle
import random
stack = []
#max_it = maximum iterations, word = starting axiom such as 'F', proc_rules are the rules that
#change the elements of word if it's key is found in dictionary notation, x and y are the
#coordinates, and turn is the starting angle
def createWord(max_it, word, proc_rules, x, y, turn):
turtle.up()
turtle.home()
turtle.goto(x, y)
turtle.right(turn)
turtle.down()
t = 0
while t < max_it:
word = rewrite(word, proc_rules)
drawit(word, 5, 20)
t = t+1
def rewrite(word, proc_rules):
#rewrite changes the word at each iteration depending on proc_rules
wordList = list(word)
for i in range(len(wordList)):
curChar = wordList[i]
if curChar in proc_rules:
wordList[i] = proc_rules[curChar]
return "".join(wordList)
def drawit(newWord, d, angle):
#drawit 'draws' the words
newWordLs = list(newWord)
for i in range(len(newWordLs)):
cur_Char = newWordLs[i]
if cur_Char == 'F':
turtle.forward(d)
elif cur_Char == '+':
turtle.right(angle)
elif cur_Char == '-':
turtle.left(angle)
elif cur_Char == '[':
state_push()
elif cur_Char == ']':
state_pop()
def state_push():
global stack
stack.append((turtle.position(), turtle.heading()))
def state_pop():
global stack
position, heading = stack.pop()
turtle.up()
turtle.goto(position)
turtle.setheading(heading)
turtle.down()
def randomStart():
#x can be anywhere from -300 to 300, all across the canvas
x = random.randint(-300, 300)
#these are trees, so we need to constrain the 'root' of each
# to a fairly narrow range from -320 to -280
y = random.randint(-320, -280)
#heading (the angle of the 'stalk') will be constrained
#from -80 to -100 (10 degrees either side of straight up)
heading = random.randint(-100, -80)
return ((x, y), heading)
def main():
#define the list for rule sets.
#each set is iteration range [i_range], the axiom and the rule for making a tree.
#the randomizer will select one of these for building.
rule_sets = []
rule_sets.append(((3, 5), 'F', {'F':'F[+F][-F]F'}))
rule_sets.append(((4, 6), 'B', {'B':'F[-B][+ B]', 'F':'FF'}))
rule_sets.append(((2, 4), 'F', {'F':'FF+[+F-F-F]-[-F+F+F]'}))
#define the number of trees to build
tree_count = 50
#speed up the turtle
turtle.tracer(10, 0)
#for each tree...
for x in range(tree_count):
#pick a random number between 0 and the length
#of the rule set -1 - this results in selecting
#a result randomly from the list of possible rules.
rand_i = random.randint(0, len(rule_sets) - 1)
selected_ruleset = rule_sets[rand_i]
#unpack the tuple stored for this ruleset
i_range, word, rule = selected_ruleset
#pick a random number inside the given iteration_range to be the
#iteration length for this command list.
low, high = i_range
i = random.randint(low, high)
#get a random starting location and heading for the tree
start_position, start_heading = randomStart()
#unpack the x & y coordinates from the position
start_x, start_y = start_position
#build the current tree
createWord(i, word, rule, start_x, start_y, start_heading)
if __name__ == '__main__': main()
2 个回答
根据我对L系统的理解,它有一套完整的规则,而不是随便选择的。你能详细讲讲你的规则是怎么运作的吗?我想你可以通过设定一个有限的、封闭的生成规则来限制树木生长的方向,比如让它们的生长角度不能超过起始角度90度。
不过这样的话,你就不能完全随机化起始角度了……你可能需要在某个范围内随机化它?当然,如果你有一个L系统是完全随机生成的,那它看起来就像一堆杂乱无章的东西。对初始条件加以限制是有意义的;每个规则都有一个起始符号,你需要利用这个起始符号来生成有意义的东西。我想你希望你的起始符号总是指向上方。
不过我已经很久没研究L系统了,所以我的回答仅供参考。
编辑:
这是一个有趣的限制,因为听起来像是树木自然生长的方式。在自然界中,我觉得这是因为只有一定量的阳光能照射到某一块土地上,所以如果一棵树已经在某个地方生长,其他植物就无法在那儿生长。
作为一个AI领域的人,我喜欢把现实世界的解决方案转化为有趣的启发式方法。我们在这里寻找什么样的启发式方法呢?在一个二维坐标系统中,“一块土地”其实就是一系列的x坐标。也许可以设定一些规则,让生长的叶子如果周围某个x范围内有太多东西,就会失去生长的动力?(不是python的xrange,而是一系列的x坐标,某种“增量”值。)
我觉得问题主要在于树木之间特征的一致性,而不是它们的具体位置。
一个可能的解决办法是加入一些变异。为了全局控制“生长受限”,你可以抑制大约5%的生产应用。这应该能让树木变得更稀疏,跟模型的关系不那么紧密。
如果想要更精细的控制,可以给每个生产应用设置不同的抑制权重。
想了解更多,可以看看《植物的算法美》第1.7节随机L系统。他们使用概率来从多个单一规则的变体中进行选择。