Compare commits

...

4 Commits

Author SHA1 Message Date
fe94e38589 add jpeg generation, refactor 2025-03-30 16:49:08 +03:00
Dmitry Fedotov
878e3d7c05 add Makefile 2025-03-25 22:18:31 +03:00
Dmitry Fedotov
eca7e24906 mv image to example dir 2025-03-25 22:11:42 +03:00
Dmitry Fedotov
e149469c6c add color variations 2025-03-25 21:09:01 +03:00
12 changed files with 246 additions and 65 deletions

2
.gitignore vendored
View File

@@ -1 +1,3 @@
barnsley-fern barnsley-fern
barnsley-fern-go
barnsley-fern.png

8
Makefile Normal file
View File

@@ -0,0 +1,8 @@
.PHONY: build, clean
build:
go build -v
clean:
rm -f barnsley-fern-go barnsley-fern.png

View File

@@ -1,12 +1,12 @@
# barnsley-fern-go # barnsley-fern-go
Implementation of Barnsley Fern in Go Implementation of Barnsley Fern in Go
![Barnsley Fern](https://code.uint32.ru/dmitry/barnsley-fern-go/raw/branch/main/barnsley_fern.png "Fern") ![Barnsley Fern](https://code.uint32.ru/dmitry/barnsley-fern-go/raw/branch/main/example/barnsley-fern.png "Fern")
From the root of the repository From the root of the repository
```bash ```bash
go build barnsley-fern.go go build
./barnsley-fern ./barnsley-fern-go
``` ```
or simply or simply
``` ```
@@ -15,3 +15,5 @@ go run barnsley-fern.go
This will generate an image in current directory. This will generate an image in current directory.
See **./barnsley-fern --help** for available flags. See **./barnsley-fern --help** for available flags.
Image on this page was generated with **./barnsley-fern-go -x 500 -y 500 -c 3 -out example/barnsley-fern.png**

View File

@@ -1,40 +1,12 @@
package main package main
import ( import (
"flag"
"fmt"
"image" "image"
"image/color"
"image/png"
"math/rand" "math/rand"
"os" "time"
) )
func createImage(h, v int) *image.RGBA { func drawBarnsleyFern(img *image.RGBA, colorfunc colorFunc, dots int) {
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 := 0; x < rect.Max.X; x++ {
for y := 0; y < rect.Max.Y; y++ {
img.Set(x, y, c)
}
}
}
func min(a, b float64) float64 {
if a > b {
a = b
}
return a
}
func drawBarnsleyFern(img *image.RGBA, c color.Color, dots int) {
var ( var (
x, y, tmpx, tmpy, r, maxy, maxx, scale, yoffset, xoffset float64 x, y, tmpx, tmpy, r, maxy, maxx, scale, yoffset, xoffset float64
) )
@@ -43,7 +15,7 @@ func drawBarnsleyFern(img *image.RGBA, c color.Color, dots int) {
scale = min(maxx, maxy) scale = min(maxx, maxy)
yoffset = scale / 10 // отступы 10% сверху и снизу yoffset = scale / 10 // отступы 10% сверху и снизу
scale = scale - yoffset*2 // масштаб самого папоротника scale = scale - yoffset*2 // масштаб самого папоротника
xoffset = (maxx - scale) / 2 // равные отступы с двух сторон xoffset = (maxx - scale) / 2 // равные отступы по сторонам
x, y = 0.5, 0.0 x, y = 0.5, 0.0
for dots > 0 { for dots > 0 {
@@ -68,39 +40,16 @@ func drawBarnsleyFern(img *image.RGBA, c color.Color, dots int) {
} }
// рисуем точку // рисуем точку
x, y = tmpx, tmpy x, y = tmpx, tmpy
img.Set(int(xoffset+x*scale), int(maxy-yoffset-y*scale), c)
color := colorfunc(x, y, time.Now())
img.Set(int(xoffset+x*scale), int(maxy-yoffset-y*scale), color)
} }
} }
func main() { func min(a, b float64) float64 {
var ( if a > b {
filename string = "barnsley_fern.png" a = b
h = flag.Int("h", 1920, "размер картинки по горизонтали")
v = flag.Int("v", 1080, "размер картинки по вертикали")
dots = flag.Int("d", 100000, "сколько точек рисовать")
img *image.RGBA
f *os.File
err error
)
flag.Parse()
img = createImage(*h, *v)
fillBackground(img, color.White)
drawBarnsleyFern(img, color.RGBA{0, 153, 0, 255}, *dots)
f, err = os.Create(filename)
if err != nil {
fmt.Printf("не удалось создать файл %s: %v", filename, err)
return
} }
return a
defer f.Close()
if err = png.Encode(f, img); err != nil {
fmt.Printf("не удалось сохранить изображение: %v", err)
return
}
fmt.Println("done...")
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

47
color.go Normal file
View File

@@ -0,0 +1,47 @@
package main
import (
"image/color"
"math/rand"
"time"
)
type colorFunc func(float64, float64, time.Time) color.Color
func newColorFunc(c colormode) colorFunc {
start := time.Now()
return func(x, y float64, t time.Time) color.Color {
var (
r, g, b, a uint8
)
switch c {
case crandom:
r = uint8(rand.Intn(256))
g = uint8(rand.Intn(256))
b = uint8(rand.Intn(256))
a = 255
case ctimed:
n := time.Since(start).Nanoseconds()
r = uint8(n % 256)
g = uint8((n + 128) % 256)
//b = uint8((n + 1) % 256)
a = 255
case crainbow:
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}
}
}

BIN
example/barnsley-fern.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

2
go.mod
View File

@@ -1,3 +1,5 @@
module code.uint32.ru/dmitry/barnsley-fern-go module code.uint32.ru/dmitry/barnsley-fern-go
go 1.24.1 go 1.24.1
require github.com/AlexEidt/Vidio v1.5.1 // indirect

2
go.sum Normal file
View File

@@ -0,0 +1,2 @@
github.com/AlexEidt/Vidio v1.5.1 h1:tovwvtgQagUz1vifiL9OeWkg1fP/XUzFazFKh7tFtaE=
github.com/AlexEidt/Vidio v1.5.1/go.mod h1:djhIMnWMqPrC3X6nB6ymGX6uWWlgw+VayYGKE1bNwmI=

68
image.go Normal file
View File

@@ -0,0 +1,68 @@
package main
import (
"errors"
"fmt"
"image"
"image/color"
"image/jpeg"
"image/png"
"io"
)
var (
errEncodeImage = errors.New("не удалось создать изображение")
errUnknownFormat = errors.New("неизвестный формат изображения")
)
func createBarnsleyFernImage(s *settings, w io.Writer) error {
img := newImageWithBackGround(s.X, s.Y, s.BG)
drawBarnsleyFern(img, newColorFunc(s.CM), s.Dots)
switch s.OM {
case outputjpeg:
opts := &jpeg.Options{
Quality: 100,
}
if err := jpeg.Encode(w, img, opts); err != nil {
return errors.Join(errEncodeImage, err)
}
case outputpng:
if err := png.Encode(w, img); err != nil {
return errors.Join(errEncodeImage, err)
}
default:
return fmt.Errorf("%w: %d", errUnknownFormat, s.OM)
}
return nil
}
func newImageWithBackGround(x, y int, background color.Color) *image.RGBA {
img := createImage(x, y)
fillBackground(img, background)
return img
}
func createImage(x, y int) *image.RGBA {
rect := image.Rectangle{
image.Point{0, 0},
image.Point{x, y},
}
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)
}
}
}

38
main.go Normal file
View File

@@ -0,0 +1,38 @@
package main
import (
"fmt"
"os"
)
func main() {
exitCode := run()
os.Exit(exitCode)
}
func run() int {
s := parseSettings()
file, err := os.Create(s.Fname)
if err != nil {
fmt.Println("ошибка открытия файла", err)
return 1
}
defer func() {
}()
if err := createBarnsleyFernImage(s, file); err != nil {
fmt.Println("ошибка создания изображения:", err)
return 2
}
if err := file.Close(); err != nil {
fmt.Println("ошибка закрытия файла:", err)
return 3
}
fmt.Println("done...")
return 0
}

63
settings.go Normal file
View File

@@ -0,0 +1,63 @@
package main
import (
"flag"
"fmt"
"image/color"
)
type colormode int
const (
cdefault colormode = iota
crandom
ctimed
crainbow
)
type outputmode int
const (
outputpng outputmode = iota
outputjpeg
)
type settings struct {
X, Y int // image size
Dots int // dots to draw for single image
BG color.Color // background color
CM colormode // settings for colorFunc
OM outputmode // what to output
Fname string // filename for output
}
func parseSettings() *settings {
var (
x, y int
dots int
cmode int
output int
fname string
)
flag.IntVar(&x, "x", 1920, "размер картинки по горизонтали")
flag.IntVar(&y, "y", 1080, "размер картинки по вертикали")
flag.IntVar(&dots, "d", 100000, "сколько точек рисовать")
flag.IntVar(&cmode, "c", 0, "color mode: 0 - default green, 1 - random, 2 - timed, 3 - rainbow")
flag.IntVar(&output, "o", 0, fmt.Sprintf("режим вывода: %d - png, %d - jpeg", outputpng, outputjpeg))
flag.StringVar(&fname, "out", "barnsley-fern.png", "полный путь файла для записи изображения")
flag.Parse()
s := &settings{
X: x,
Y: y,
Dots: dots,
BG: color.White,
CM: colormode(cmode),
OM: outputmode(output),
Fname: fname,
}
return s
}