首页 > 代码库 > 分形算法小记

分形算法小记

模式替换的分形,通常都可以使用L系统表示,想那些雪花曲线之类的都是模式替换分形的。L系统是指一个字符序列,这个字符序列内的某些子序列可以按照规则替换为特定的序列(通常更长),序列中的不同字符有不同的含义。比如改变绘制方向和绘制一条线段等等。

通过L系统可以绘制出非常接近真实的植物来,当然也可以绘制出其他的曲线。

package main

import (
    "github.com/hydra13142/paint"
    "image"
    "image/color"
    "image/png"
    "os"
)

const (
    D = 6
)

var (
    X, Y, N = 6, 1536, 0
    img     = paint.Image{image.NewRGBA(image.Rect(0, 0, 1543, 1543)), color.RGBA{50, 125, 50, 255}, color.RGBA{50, 50, 100, 255}}
)

type Lsystem struct {
    rp map[byte][]byte
    do map[byte]func()
    ch chan byte
}

func NewLsystem(a map[byte]string, b map[byte]func()) *Lsystem {
    c := make(map[byte][]byte, len(a))
    for k, v := range a {
        c[k] = []byte(v)
    }
    return &Lsystem{c, b, nil}
}

func (l *Lsystem) Init(s string, n int) {
    step := func(in <-chan byte) chan byte {
        ex := make(chan byte)
        go func() {
            for {
                c, ok := <-in
                if !ok {
                    break
                }
                s, ok := l.rp[c]
                if !ok {
                    ex <- c
                } else {
                    for _, c = range s {
                        ex <- c
                    }
                }
            }
            close(ex)
        }()
        return ex
    }
    var p, q chan byte
    p = make(chan byte)
    for q = p; n > 0; n-- {
        q = step(q)
    }
    l.ch = q
    go func() {
        for _, c := range []byte(s) {
            p <- c
        }
        close(p)
    }()
}

func (l *Lsystem) Run() {
    for {
        c, ok := <-l.ch
        if !ok {
            break
        }
        f, ok := l.do[c]
        if ok {
            f()
        }
    }
}

func main() {
    // 绘制背景色
    img.Bar(0, 0, 1542, 1542)

    draw := func() {
        var x, y int
        switch N % 4 {
        case 1, -3:
            x, y = X, Y+D
        case 2, -2:
            x, y = X-D, Y
        case 3, -1:
            x, y = X, Y-D
        case 0:
            x, y = X+D, Y
        }
        img.Line(X, Y, x, y)
        X, Y = x, y
    }

    lsys := NewLsystem(
        map[byte]string{
            ‘L‘: "+RF-LFL-FR+",
            ‘R‘: "-LF+RFR+FL-",
        },
        map[byte]func(){
            ‘+‘: func() { N++ },
            ‘-‘: func() { N-- },
            ‘F‘: func() { draw() },
        },
        /*
            如果换成下面两个参数,则会绘制龙形曲线
            map[byte]string{
                ‘A‘: "-A+B+A-B",
                ‘B‘: "A+B-A-B+",
            },
            map[byte]func(){
                ‘+‘: func() { N++ },
                ‘-‘: func() { N-- },
                ‘A‘: func() { draw() },
                ‘B‘: func() { draw() },
            },
        */
    )
    lsys.Init("R", 8)
    lsys.Run()

    // 写入文件保存
    f, e := os.Create("frac.png")
    if e != nil {
        println("error")
    }
    defer f.Close()
    png.Encode(f, img)
}

以上是一个模式匹配分形的例子,绘出来的图还是很漂亮的,可以作壁纸/桌面,如下:

模式匹配分形

这个小程序里,我使用了串联的管道来进行多次的模式替换,如果是c++等语言,则需要采用类似任务队列的方式来进行处理(或者也使用多线程)。go可以用管道,可以节省很多内存。

另一种分形是迭代式分形。这种分形不是严格自相似的,但往往更漂亮。

package main

import (
    "image"
    "image/color"
    "image/png"
    //"math"
    "os"
)

func repeat(i, j int) color.RGBA {
    x := float64(i-3500) / 2000
    y := float64(j-2500) / 2000
    a := 0.0
    b := 0.0
    for t := 0; t < 256; t++ {
        m := a * a
        n := b * b
        o := a * b
        a = m - n + x
        b = o + o + y
        if m+n > 4 {
            return color.RGBA{uint8(t), uint8(t), uint8(t), 255}
        }
    }
    return color.RGBA{255, 255, 255, 255}
}

func main() {
    file, _ := os.Create("mdb.png")
    defer file.Close()
    img := image.NewRGBA(image.Rect(0, 0, 5000, 5000))
    defer png.Encode(file, img)

    for i := 0; i < 5000; i++ {
        for j := 0; j < 5000; j++ {
            c := repeat(i, j)
            img.Set(i, j, c)
        }
    }
}

最常见的迭代式分形。

以下是绘制结果的一部分(完整的图比这个大多了)

迭代式分形

分形算法小记