Files
barnsley-fern-go/barnsley-fern.go
2025-03-25 20:53:43 +03:00

165 lines
3.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package main
import (
"flag"
"fmt"
"image"
"image/color"
"image/png"
"math/rand"
"os"
"time"
)
func createImage(h, v int) *image.RGBA {
rect := image.Rectangle{
image.Point{0, 0},
image.Point{h, v},
}
return image.NewRGBA(rect)
}
func fillBackground(img *image.RGBA, c color.Color) {
rect := img.Bounds()
for x := range rect.Max.X {
for y := range rect.Max.Y {
img.Set(x, y, c)
}
}
}
func newColorFunc(random bool, timed bool, rainbow bool) func(float64, float64) color.Color {
return func(x, y float64) color.Color {
var (
r, g, b, a uint8
)
switch {
case random:
// n := rand.Intn(3)
// switch n {
// case 0:
// r = 255
// case 1:
// g = 255
// case 2:
// b = 255
// }
r = uint8(rand.Intn(256))
g = uint8(rand.Intn(256))
b = uint8(rand.Intn(256))
a = 255
case timed:
n := time.Now().Nanosecond() % 256 // Nanosecond returns range [0, 999999999]
r = uint8((n + 256) % 256)
g = uint8((n + 128) % 256)
b = uint8(n % 256)
a = 255
case rainbow:
r = uint8(y * 256)
g = uint8(x * 256)
b = uint8(((y - x) - min(x, y)) * 256)
a = 255
default:
r, g, b, a = 0, 153, 0, 255
}
return color.RGBA{r, g, b, a}
}
}
func min(a, b float64) float64 {
if a > b {
a = b
}
return a
}
func drawBarnsleyFern(img *image.RGBA, colorfunc func(float64, float64) color.Color, dots int) {
var (
x, y, tmpx, tmpy, r, maxy, maxx, scale, yoffset, xoffset float64
)
maxy = float64(img.Bounds().Max.Y) // размер картинки по вертикали
maxx = float64(img.Bounds().Max.X) // размер картиники по горизонтали
scale = min(maxx, maxy)
yoffset = scale / 10 // отступы 10% сверху и снизу
scale = scale - yoffset*2 // масштаб самого папоротника
xoffset = (maxx - scale) / 2 // равные отступы с двух сторон
x, y = 0.5, 0.0
for dots > 0 {
dots--
r = rand.Float64()
if r <= 0.01 {
// стебель
tmpx = 0.5
tmpy = 0.16 * y
} else if r <= 0.08 {
// самый большой правый листок
tmpx = 0.2*x - 0.26*y + 0.400
tmpy = 0.23*x + 0.22*y - 0.045
} else if r <= 0.15 {
// самый большой левый листок
tmpx = -0.15*x + 0.28*y + 0.575
tmpy = 0.26*x + 0.24*y - 0.086
} else {
// последующие листочки
tmpx = 0.85*x + 0.04*y + 0.075
tmpy = -0.04*x + 0.850*y + 0.180
}
// рисуем точку
x, y = tmpx, tmpy
color := colorfunc(x, y)
img.Set(int(xoffset+x*scale), int(maxy-yoffset-y*scale), color)
}
}
func main() {
var (
filename string = "barnsley_fern.png"
h = flag.Int("h", 1920, "размер картинки по горизонтали")
v = flag.Int("v", 1080, "размер картинки по вертикали")
dots = flag.Int("d", 100000, "сколько точек рисовать")
rand = flag.Bool("rand", false, "использовать случайные цвета")
timed = flag.Bool("timed", false, "привязка к наносекундам времени исполнения")
rainbow = flag.Bool("rainbow", false, "привязка к координатам")
img *image.RGBA
f *os.File
err error
)
flag.Parse()
img = createImage(*h, *v)
fillBackground(img, color.White)
colorfunc := newColorFunc(*rand, *timed, *rainbow)
drawBarnsleyFern(img, colorfunc, *dots)
f, err = os.Create(filename)
if err != nil {
fmt.Printf("не удалось создать файл %s: %v", filename, err)
return
}
defer f.Close()
if err = png.Encode(f, img); err != nil {
fmt.Printf("не удалось сохранить изображение: %v", err)
return
}
fmt.Println("done...")
}