package main import ( "fmt" "image" "image/color" "image/png" "log" "math" "math/cmplx" "net/http" "runtime" "strconv" "strings" "sync" "deadbeef.codes/steven/mandelmapper/palette" ) const ( Size = 128 Iterations = (1<<16 - 1) / 128 ) var ( colors []color.RGBA ) func mandelbrot(c complex128) uint16 { var z complex128 for i := 0; i < Iterations; i++ { z = z*z + c if cmplx.IsNaN(z) { return uint16(i) } } return Iterations } type pixel struct { out *image.RGBA x, y int tileX, tileY int64 tileZoom uint16 wg *sync.WaitGroup } var queue = make(chan pixel) func computeThread() { for p := range queue { val := mandelbrot( complex( (float64(p.x)/Size+float64(p.tileX))/float64(uint(1<<(p.tileZoom-1))), (float64(p.y)/Size+float64(p.tileY))/float64(uint(1<<(p.tileZoom-1))), ), ) p.out.SetRGBA(p.x, p.y, colors[val]) p.wg.Done() } } func main() { runtime.GOMAXPROCS(runtime.NumCPU()) for i := 0; i < runtime.GOMAXPROCS(0); i++ { go computeThread() } colorStep := float64(Iterations) colors = interpolateColors("Plan9", colorStep) fmt.Println(len(colors)) log.Fatal(http.ListenAndServe(":6161", nil)) } func renderTile(w http.ResponseWriter, r *http.Request) { components := strings.Split(r.URL.Path, "/")[1:] if len(components) != 4 || components[0] != "mandelbrot" || components[3][len(components[3])-4:] != ".png" { w.WriteHeader(http.StatusNotFound) return } components[3] = components[3][:len(components[3])-4] tileX, err := strconv.ParseInt(components[2], 10, 64) if err != nil { w.WriteHeader(http.StatusNotFound) return } tileY, err := strconv.ParseInt(components[3], 10, 64) if err != nil { w.WriteHeader(http.StatusNotFound) return } tileZoom, err := strconv.ParseUint(components[1], 10, 8) if err != nil { w.WriteHeader(http.StatusNotFound) return } var wg sync.WaitGroup wg.Add(Size * Size) img := image.NewRGBA(image.Rect(0, 0, Size, Size)) for x := 0; x < Size; x++ { for y := 0; y < Size; y++ { queue <- pixel{img, x, y, tileX - int64(1<= steps[j] && i < steps[j+1] { min = steps[j] max = steps[j+1] minColor = float64(cols[j]) maxColor = float64(cols[j+1]) uintColor := cosineInterpolation(maxColor, minColor, (i-min)/(max-min)) interpolated = append(interpolated, uint32(uintColor)) } } } } for _, pixelValue := range interpolated { r := pixelValue >> 24 & 0xff g := pixelValue >> 16 & 0xff b := pixelValue >> 8 & 0xff a := 0xff interpolatedColors = append(interpolatedColors, color.RGBA{uint8(r), uint8(g), uint8(b), uint8(a)}) } } } } return interpolatedColors } func cosineInterpolation(c1, c2, mu float64) float64 { mu2 := (1 - math.Cos(mu*math.Pi)) / 2.0 return c1*(1-mu2) + c2*mu2 } func linearInterpolation(c1, c2, mu uint32) uint32 { return c1*(1-mu) + c2*mu } //Adds a text label to an image /* func addLabel(img *image.RGBA, x, y int, label string) { col := color.RGBA{255, 255, 255, 255} point := fixed.Point26_6{fixed.Int26_6(x * 64), fixed.Int26_6(y * 64)} d := &font.Drawer{ Dst: img, Src: image.NewUniform(col), Face: basicfont.Face7x13, Dot: point, } d.DrawString(label) } func GetOutboundIP() net.IP { conn, err := net.Dial("udp", "8.8.8.8:80") if err != nil { log.Fatal(err) } defer conn.Close() localAddr := conn.LocalAddr().(*net.UDPAddr) return localAddr.IP } */