{"id":15394234,"url":"https://github.com/xyproto/mandel","last_synced_at":"2025-03-27T23:44:02.367Z","repository":{"id":166495970,"uuid":"641996735","full_name":"xyproto/mandel","owner":"xyproto","description":"Fractal renderers","archived":false,"fork":false,"pushed_at":"2023-05-24T00:29:30.000Z","size":1430,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-02-02T01:46:24.134Z","etag":null,"topics":["fractal","go","mandelbrot","mandelbulb","png","renderer","supersampling"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"cc0-1.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/xyproto.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-05-17T15:40:15.000Z","updated_at":"2023-05-17T15:57:17.000Z","dependencies_parsed_at":"2023-08-25T03:48:28.196Z","dependency_job_id":null,"html_url":"https://github.com/xyproto/mandel","commit_stats":null,"previous_names":["xyproto/mandel"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xyproto%2Fmandel","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xyproto%2Fmandel/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xyproto%2Fmandel/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xyproto%2Fmandel/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/xyproto","download_url":"https://codeload.github.com/xyproto/mandel/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245944062,"owners_count":20697948,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["fractal","go","mandelbrot","mandelbulb","png","renderer","supersampling"],"created_at":"2024-10-01T15:22:37.242Z","updated_at":"2025-03-27T23:44:02.339Z","avatar_url":"https://github.com/xyproto.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Mandel\n\nTwo short Go programs that demonstrates the use of concurrency and goroutines for rendering a Mandelbrot fractal and a slice of a Mandelbulb fractal.\n\nEach row is rendered in a goroutine, where they are spawned with the `go` keyword. `sync.WaitGroup` is used to wait for the gorutines to complete. Only the Go standard library is used, for both programs.\n\n## Screenshots\n\n### Mandelbrot\n\n![Mandelbrot](img/mandelbrot.png)\n\nThis image is rendered at 3840x2160 and takes around 2 seconds to render on my laptop.\n\n### Mandelbulb\n\n![Mandelbulb](img/mandelbulb.png)\n\nThis image is rendered at 3840x2160 and then scaled down to 1920x1080. It takes around 12 seconds to render on my laptop.\n\n## Source code\n\n### Mandelbrot\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"image\"\n    \"image/color\"\n    \"image/png\"\n    \"math/cmplx\"\n    \"os\"\n    \"sync\"\n)\n\nconst (\n    width      = 3840\n    height     = 2160\n    xmin, ymin = -2, -1\n    xmax, ymax = 1, 1\n    maxIter    = 1000\n)\n\nvar wg sync.WaitGroup\n\n// mandelbrot calculates the color of a point in the Mandelbrot set.\nfunc mandelbrot(c complex128) color.Color {\n    z := c\n    for i := 0; i \u003c maxIter; i++ {\n        if cmplx.Abs(z) \u003e 2 {\n            return color.Gray{uint8(255 - i%256)}\n        }\n        z = z*z + c\n    }\n    return color.Black\n}\n\n// renderRow renders a single row of the Mandelbrot set.\nfunc renderRow(img *image.RGBA, y int) {\n    defer wg.Done()\n    for x := 0; x \u003c width; x++ {\n        c := complex(\n            float64(x)/width*(xmax-xmin)+xmin,\n            float64(y)/height*(ymax-ymin)+ymin)\n        color := mandelbrot(c)\n        img.Set(x, y, color)\n    }\n}\n\nfunc main() {\n    img := image.NewRGBA(image.Rect(0, 0, width, height))\n\n    for y := 0; y \u003c height; y++ {\n        wg.Add(1)\n        go renderRow(img, y)\n    }\n\n    wg.Wait()\n\n    file, err := os.Create(\"mandelbrot.png\")\n    if err != nil {\n        fmt.Println(\"Error creating file:\", err)\n        return\n    }\n    defer file.Close()\n\n    err = png.Encode(file, img)\n    if err != nil {\n        fmt.Println(\"Error encoding image:\", err)\n    }\n}\n```\n\n### Mandelbulb\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"image\"\n    \"image/color\"\n    \"image/png\"\n    \"math\"\n    \"os\"\n    \"sync\"\n)\n\nconst (\n    width, height      = 1920, 1080\n    ssWidth, ssHeight  = 3840, 2160\n    aspectRatio        = float64(ssWidth) / float64(ssHeight)\n    fov                = math.Pi / 8\n    maxIter            = 1000\n    power              = 9\n    escapeRadius       = 1.6\n    supersamplingRatio = 2\n)\n\nvar wg sync.WaitGroup\n\ntype vec3 struct{ x, y, z float64 }\n\nfunc (v vec3) add(w vec3) vec3    { return vec3{v.x + w.x, v.y + w.y, v.z + w.z} }\nfunc (v vec3) mul(s float64) vec3 { return vec3{v.x * s, v.y * s, v.z * s} }\nfunc (v vec3) length() float64    { return math.Sqrt(v.x*v.x + v.y*v.y + v.z*v.z) }\nfunc (v vec3) normalize() vec3    { return v.mul(1 / v.length()) }\n\nfunc mandelbulb(p vec3) float64 {\n    z, r, theta, phi := p, 0.0, 0.0, 0.0\n    for i := 0; i \u003c maxIter; i++ {\n        r = z.length()\n        if r \u003e escapeRadius {\n            break\n        }\n        theta, phi = math.Acos(z.z/r)*power, math.Atan2(z.y, z.x)*power\n        r = math.Pow(r, power)\n        z.x = p.x + r*math.Sin(theta)*math.Cos(phi)\n        z.y = p.y + r*math.Sin(theta)*math.Sin(phi)\n        z.z = p.z + r*math.Cos(theta)\n    }\n    return r\n}\n\nfunc renderPixel(x, y int) color.Color {\n    px := (2*float64(x)/float64(ssWidth) - 1) * aspectRatio * math.Tan(fov/2)\n    py := (1 - 2*float64(y)/float64(ssHeight)) * math.Tan(fov/2)\n    direction := vec3{px, py, -1}.normalize()\n    t := 0.0\n    for i := 0; i \u003c maxIter; i++ {\n        p := direction.mul(t).add(vec3{0, 0, 5})\n        d := mandelbulb(p) - 1\n        if d \u003c 0.001 {\n            return color.Gray{uint8(255 - t*255/100)}\n        }\n        t += d\n        if t \u003e 100 {\n            break\n        }\n    }\n    return color.Black\n}\n\nfunc main() {\n    ssImg := image.NewRGBA(image.Rect(0, 0, ssWidth, ssHeight))\n    for y := 0; y \u003c ssHeight; y++ {\n        wg.Add(1)\n        go func(y int) {\n            defer wg.Done()\n            for x := 0; x \u003c ssWidth; x++ {\n                ssImg.Set(x, y, renderPixel(x, y))\n            }\n        }(y)\n    }\n    wg.Wait()\n\n    img := image.NewRGBA(image.Rect(0, 0, width, height))\n    for y := 0; y \u003c height; y++ {\n        for x := 0; x \u003c width; x++ {\n            r, g, b, a := 0, 0, 0, 0\n            for dy := 0; dy \u003c supersamplingRatio; dy++ {\n                for dx := 0; dx \u003c supersamplingRatio; dx++ {\n                    c := ssImg.RGBAAt(x*supersamplingRatio+dx, y*supersamplingRatio+dy)\n                    r += int(c.R)\n                    g += int(c.G)\n                    b += int(c.B)\n                    a += int(c.A)\n                }\n            }\n            r /= supersamplingRatio * supersamplingRatio\n            g /= supersamplingRatio * supersamplingRatio\n            b /= supersamplingRatio * supersamplingRatio\n            a /= supersamplingRatio * supersamplingRatio\n            img.Set(x, y, color.RGBA{uint8(r), uint8(g), uint8(b), uint8(a)})\n        }\n    }\n\n    file, err := os.Create(\"mandelbulb.png\")\n    if err != nil {\n        fmt.Println(\"Error creating file:\", err)\n        return\n    }\n    defer file.Close()\n    err = png.Encode(file, img)\n    if err != nil {\n        fmt.Println(\"Error encoding image:\", err)\n    }\n}\n```\n\n## General info\n\n* Author: Alexander F. Rødseth\n* Assistant: GPT4\n* License: CC0\n* Version: 0.0.1\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxyproto%2Fmandel","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fxyproto%2Fmandel","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxyproto%2Fmandel/lists"}