分形算法小记

原创
2014/12/09 00:28
阅读数 657

模式替换的分形,通常都可以使用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)
		}
	}
}

最常见的迭代式分形。

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

迭代式分形

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
0 评论
9 收藏
0
分享
返回顶部
顶部