Skip to content

Instantly share code, notes, and snippets.

@motoki317
Last active March 13, 2021 12:07
Show Gist options
  • Save motoki317/d4921dd4c48096ac7e21c80a40ab5272 to your computer and use it in GitHub Desktop.
Save motoki317/d4921dd4c48096ac7e21c80a40ab5272 to your computer and use it in GitHub Desktop.
Mandelbrot Set
package main
import (
"fmt"
"github.com/lucasb-eyer/go-colorful"
"github.com/schollz/progressbar/v3"
"image"
"image/color"
"image/png"
"math"
"math/cmplx"
"os"
"sync"
)
type setting struct {
start complex128
leftTop, rightDown complex128
step float64
maxIteration int
}
const maxConcurrency = 4
var s = []*setting{
{
start: 0 + 0i,
leftTop: -2 + 1.5i,
rightDown: 1 - 1.5i,
step: 0.001,
maxIteration: 2500,
},
{
start: 0 + 0i,
leftTop: -0.671875 + 0.46875i,
rightDown: -0.65625 + 0.453125i,
step: 5e-06,
maxIteration: 2500,
},
{
start: 0 + 0i,
leftTop: -0.6640625 + 0.46875i,
rightDown: -0.65625 + 0.4609375i,
step: 2.5e-06,
maxIteration: 2500,
},
{
start: 0 + 0i,
leftTop: -0.66015625 + 0.46875i,
rightDown: -0.65625 + 0.46484375i,
step: 2.5e-06,
maxIteration: 2500,
},
{
start: 0 + 0i,
leftTop: -0.658203125 + 0.46875i,
rightDown: -0.65625 + 0.466796875i,
step: 1e-06,
maxIteration: 2500,
},
}
func main() {
for _, ss := range s[:1] {
generateImage(ss)
}
}
func generateImage(s *setting) {
width := int(math.Floor((real(s.rightDown) - real(s.leftTop)) / s.step))
height := int(math.Floor((imag(s.leftTop) - imag(s.rightDown)) / s.step))
fmt.Printf("Generating %vx%v image...\n", width, height)
bar := progressbar.Default(int64(width * height))
img := image.NewRGBA(image.Rect(0, 0, width, height))
concurrency := make(chan struct{}, maxConcurrency)
wg := sync.WaitGroup{}
for i := 0; i < width; i++ {
finalI := i
wg.Add(1)
go func() {
concurrency <- struct{}{}
for j := 0; j < height; j++ {
paintPixel(s, finalI, j, s.start, img)
}
<- concurrency
wg.Done()
_ = bar.Add(height)
}()
}
wg.Wait()
_ = bar.Finish()
name := fmt.Sprintf("leftTop%v,%v_rightDown%v,%v_size%vx%v_step%v_itr%v.png",
real(s.leftTop), imag(s.leftTop), real(s.rightDown), imag(s.rightDown),
width, height, s.step, s.maxIteration)
fmt.Printf("Outputting as file %v...\n", name)
f, err := os.Create(name)
if err != nil {
panic(err)
}
err = png.Encode(f, img)
if err != nil {
panic(err)
}
fmt.Println("Done!")
}
func paintPixel(s *setting, i int, j int, start complex128, img *image.RGBA) {
c := complex(real(s.leftTop)+float64(i)*s.step, imag(s.leftTop)-float64(j)*s.step)
if it := iterationsBeforeDiverge(s.maxIteration, start, c); it == -1 {
img.Set(i, j, color.Black)
} else {
img.Set(i, j, toHSL(it))
}
}
func toHSL(it int) color.Color {
return colorful.Hsl(float64(it%256), 0.75, 0.5)
}
func toMonochromatic(it int) color.Color {
// Normalize number of iterations to 0 ~ 1 value
normalized := 1 - math.Pow(math.E, float64(1 - it) * 0.02)
v := uint8(normalized * 255.0)
return color.RGBA{
R: v,
G: v,
B: v,
A: 255,
}
}
// Returns number of iterations before abs(z) goes over 2, returns -1 if converges.
func iterationsBeforeDiverge(maxIteration int, start, c complex128) int {
z := start
for i := 0; i < maxIteration; i++ {
z = f(z, c)
if cmplx.Abs(z) > 2 {
return i + 1
}
}
return -1
}
func f(z, c complex128) complex128 {
return z * z + c
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment