模式替换的分形,通常都可以使用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)
}
}
}
最常见的迭代式分形。
以下是绘制结果的一部分(完整的图比这个大多了)