用Small Basic来绘制分形图

十月 30th, 2008 by Soloman | Print 用Small Basic来绘制分形图 | 1,092 views

前几天,笔记本的屏幕碎了,虽然心痛,但破碎的屏幕让我联想到了很早以前见过的分形图形,但以前没怎么研究过这个东西,这次却产生了玩玩这个的念头。没过两天,无意间又发现了Small Basic: 一个类似LOGO语言的语言,又让我回忆起了小时候玩过的海龟先生。于是,这两个念头一结合:何不用Small Basic的海龟来画画分形图呢?

上网搜索了一些分形图的基本知识,这里先不提那些抽象的教条,咱们来看个实例吧。下面是我在wikipedia上找到的Koch Square图形的l-system定义:

  • 变量:F
  • 常量:+ -
  • 开始形状:F
  • 替换规则:F = F + F - F - F + F

不要怕,呵呵,其实这就是一个l-system定义,它描述了一个叫做Koch Square的图形。让我们看看它怎么描述这个图形吧。F是指向前画一个单位的长度;+号代表逆时针旋转90度;-号代表顺时针旋转90度。一个分型图需要两个条件:一个是起始形状,另一个则是如何变幻的规则。在我们这个例子中,起始形状就是向前画一条线,而变幻规则则是一系列的动作:向前画一单元;逆时针旋转90度;再画一单元的线;顺时针旋转90度;…

来看看例子吧,设n为我们准备将替换规则迭代到起始形状上的次数:

当 n=0 时,也就是不将替换规则迭代到开始形状中去,换句话说,就是我们的起始形状:

这时,我们完成这个图形的动作很简单,就是: F

好,当n=1时,我们需要将替换规则迭代一次到开始形状中去,将会产生:

完成这个图形的动作就是: F + F - F - F + F, 也就是一次迭代规则。

好,当n=2时,我们将上式中的所有F再次迭代为迭代规则里的动作,这样,动作就比较多了,是:

F+F-F-F+F+F+F-F-F+F-F+F-F-F+F-F+F-F-F+F+F+F-F-F+F

产生的图形如下:

嗯,很明显,继续迭代下去,这动作就太多了,而且这么机械的动作(移动,旋转)不正好适合我们的海龟先生来做么?

好,根据这个规则,我们写下面一段Small Basic代码:

' file: koch_square.sb
' Mr.Turtle will draw a Koch Square for us
' l-system rules:
'   variables : F
'   constants : + -
'   start : F
'   rules : F = F + F - F - F + F

' set some default values
angle = 90
length = 3
iteration = 5

' set the graphic panel color
GraphicsWindow.BackgroundColor = "Black"
GraphicsWindow.PenColor = "LightGreen"

'  move Mr.Turtle to the left-down corner for start
Turtle.Speed = 9
Turtle.Turn(-90)
Turtle.PenUp()
Turtle.Move(300)
Turtle.Turn(-90)
Turtle.Move(200)
Turtle.Turn(-90)
Turtle.PenDown()

' let's do it!
DrawStart()

' draw one rule
' if iteration is 0, stop further recursion
' otherwise, do recursion
Sub DrawRule
    iteration = iteration - 1
    If (iteration = 0) Then
        Turtle.Move(length)
        Turtle.Turn(-angle)
        Turtle.Move(length)
        Turtle.Turn(angle)
        Turtle.Move(length)
        Turtle.Turn(angle)
        Turtle.Move(length)
        Turtle.Turn(-angle)
        Turtle.Move(length)
    Else
        DrawRule()
        Turtle.Turn(-angle)
        DrawRule()
        Turtle.Turn(angle)
        DrawRule()
        Turtle.Turn(angle)
        DrawRule()
        Turtle.Turn(-angle)
        DrawRule()
    EndIf
    iteration = iteration + 1
EndSub

' draw the start shape
Sub DrawStart
    DrawRule()
EndSub

其中,iteration为迭代次数n,我们将其设为5(这个会比较耗时间,你可以尝试将其设为3),然后让海龟先生为我们“绣”出一副最简单的分形图吧(因为海龟先生画的很慢很仔细,所以我可以相信他是在“绣花”)。

下图为迭代5次的结果:

嗯,没错,这就是一幅传说中的分形图了,虽然是很简单的,但我们也可以看出一些分形图的特征:

  • 自相似性:取线段的某一小块,其形状和整体形状很相似
  • 无限细分:嗯,理论上,无论你将某部分线段放大,都是一个曲折的线段,理论上你不可能得到一条完全直的线段,当然,我们的海龟先生的生命是有限的,它无法将有限的生命投入到无限的锈图之中。

在l-system中,起始形状叫做Axiom,迭代规则叫做motif。我们再来看看另一个l-system定义的图形:Koch Curve:

Koch {
  Angle 60
  Axiom F - - F - - F
  F = F + F - - F + F
}

这里,我们的角度不再是90度了,而是60度,起始形状是个等边三角形:

那么,让海龟先生为我们画画吧:

' file: koch_curve.sb
' Mr.Turtle will draw a Koch Curve for us
' l-system rules:
'   variables : F
'   angle : 60
'   constants : + -
'   start : F - - F - - F
'   rules : F = F + F - - F + F

' set some default values
angle = 60
length = 5
iteration = 4

' set the graphic panel color
GraphicsWindow.BackgroundColor = "Black"
GraphicsWindow.PenColor = "LightGreen"

'  move Mr.Turtle to the left-down corner for start
Turtle.Speed = 9
Turtle.PenUp()
Turtle.Turn(90)
Turtle.Move(150)
Turtle.Turn(90)
Turtle.Move(150)
Turtle.Turn(90)
Turtle.PenDown()

' let's do it!
DrawStart()

' hide Mr.Turtle
Turtle.Hide()

' draw one rule
' if iteration is 0, stop further recursion
' otherwise, do recursion
Sub DrawRule
    iteration = iteration - 1
    If (iteration = 0) Then
        Turtle.Move(length)
        Turtle.Turn(-angle)
        Turtle.Move(length)
        Turtle.Turn(angle)
        Turtle.Turn(angle)
        Turtle.Move(length)
        Turtle.Turn(-angle)
        Turtle.Move(length)
    Else
        DrawRule()
        Turtle.Turn(-angle)
        DrawRule()
        Turtle.Turn(angle)
        Turtle.Turn(angle)
        DrawRule()
        Turtle.Turn(-angle)
        DrawRule()
    EndIf
    iteration = iteration + 1
EndSub

' draw the start shape
Sub DrawStart
    DrawRule()
    Turtle.Turn(angle)
    Turtle.Turn(angle)
    DrawRule()
    Turtle.Turn(angle)
    Turtle.Turn(angle)
    DrawRule()
EndSub
    

下面是迭代3次的图形:

下面是迭代4次的图形:

也许你会说了:老所怎么这么空啊,画这么多无聊的线段,其实,由简单的规则创造复杂的图形,这是很有趣的事情,比如以下这些图,都是l-system做出来的,而且是3D的,l-system在模拟植物方面非常有效。

当然,网上还有很多不错的作品,而且,l-system只是分形图形的一种,还有其他的诸如函数迭代等方法,这些方法,都能产生非常漂亮的图形,嗯,就等我有时间了再研究吧。

, ,

 

本文有27条评论

  1. leehow 说: [回复]

    早起的人儿有沙发!化悲痛为智慧,很强大,你也很有耐心。

  2. 自然堂 说: [回复]

    老时建议老所开个培训班啊。

  3. 大学生乱弹琴 说: [回复]

    经Shawn推荐到这里,学习了~

  4. Soloman 说: [回复]

    @大学生乱弹琴 看了你的帖子,好巧,同时写了一个关于分形图的帖子:)

  5. Soloman 说: [回复]

    @自然堂 呵呵,多谢啦,我希望自己写的东西都尽量简单,通俗易懂,容易上手。

  6. Soloman 说: [回复]

    @leehow 嗯,同意,要学神功,必先找刺激,哈哈

  7. 醉倚西风 说: [回复]

    你的评论框怎么搞的啊

  8. leehow 说: [回复]

    @Soloman 那看来你人生中的刺激还不少。

  9. Soloman 说: [回复]

    @leehow 这是夸我呢?还是骂我呢?晕~

  10. basil 说: [回复]

    我是过来受刺激的

  11. leehow 说: [回复]

    @Soloman 你想多了,明显是在夸你,我连受刺激的资格都没有。

  12. Soloman 说: [回复]

    @leehow 我帮你,你们家什么东西贵,给我,我帮你砸,哈哈~

  13. leehow 说: [回复]

    @Soloman 那我真是受宠若惊啊,可惜只有几个锅碗瓢盆。

  14. Shawn 说: [回复]

    @Soloman 我推荐人家来,结果我自己忘了来。。。

  15. Soloman 说: [回复]

    @Shawn 你都推荐了人了,你自己肯定是来过散,肯定看过散

  16. Shawn 说: [回复]

    @Soloman 看过,但是没多大兴趣。。。我的意思是,我忘了留言。

  17. iColor 说: [回复]

    长见识,,但是看着头疼,
    我原来也学过一段时间程序,但因为过于注重UI而暂时放弃了,
    我说老所你是不是会写个Ubuntu新版的文章呢

  18. jk 说: [回复]

    学习了,能加gt,神侃否!

  19. Jor 说: [回复]

    哇,原来数学如此美丽…… 哈哈!

  20. Soloman 说: [回复]

    @iColor 这段程序是非常容易读的,直接将L-System的公式套成程序就搞定了。我UI也做不好,以前用MFC,太麻烦了,最近看上了wxWidget这个界面库,支持很多语言的。

    Ubuntu新版?其实你如果知道apt的工作原理,这些版本都不存在,只存在一个持续维护的主干版,不过也分三个:stable, testing, sid。我喜欢Debian以及Debian儿子Ubuntu的地方就在这,安装一次后,安装盘就可以仍了,呵呵。

  21. Soloman 说: [回复]

    @Jor 恩,还有更漂亮和更有意思的用法,待我慢慢写来哈,呵呵~

  22. Yacca 说: [回复]

    潜心研究技术的人可敬啊…未来的高科技靠你们了…

  23. Soloman 说: [回复]

    @Yacca 潜心研究美剧的人啊…帮我分析每部看得半懂的美剧的任务就靠你啦…

  24. daniel 说: [回复]

    就是我不学这个,不然我就靠你啦 。。。O(∩_∩)O哈哈~

添加评论 (支持Gravatar头像)

注意: 评论者允许使用'@user空格'的方式将自己的评论通知另外评论者。例如, ABC是本文的评论者之一,则使用'@ABC '(不包括单引号)将会自动将您的评论发送给ABC。使用'@all ',将会将评论发送给之前所有其它评论者。请务必注意user必须和评论者名相匹配(大小写一致)。

实时评论预览