Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
gomandel
*.png
*.jpg
go.sum
go.mod
.vscode
17 changes: 17 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@

FROM ubi8/go-toolset:1.18 as build

### Copy source code for building the application
COPY ./mandel.go .

### Download dependencies and build
RUN go mod init gomandel && \
go mod tidy -e && \
go build .

FROM ubi8/ubi-micro
COPY --from=build /opt/app-root/src/gomandel .

EXPOSE 8080
ENTRYPOINT ["./gomandel"]
CMD ["--server", "--xres=1024", "--yres=1024" ]
Binary file removed example.png
Binary file not shown.
143 changes: 110 additions & 33 deletions mandel.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,27 @@ import (
"image/color"
"image/color/palette"
"image/png"
"log"
"math"
"math/rand"
"net/http"
"os"
"sort"
"strconv"
"sync"

"github.com/nfnt/resize"
)

const RADIUS_MIN = 0.0005
const RADIUS_MAX = 1.0

var IT, xres, yres, aa int
var xpos, ypos, radius float64
var out_filename, palette_string string
var invert bool
var invert, server_mode, randomize bool
var focusstring string
var port string

func init() {
flag.IntVar(&IT, "IT", 512, "maximum number of iterations")
Expand All @@ -33,11 +41,18 @@ func init() {
flag.StringVar(&palette_string, "palette", "plan9", "One of: plan9|websafe|gameboy|retro|alternate")
flag.StringVar(&focusstring, "focus", "", "sequence of focus command. Select quadrant (numbered 1-4). e.g.: 1423. Read code to understand")
flag.BoolVar(&invert, "invert", false, "Inverts colouring")
flag.BoolVar(&server_mode, "server", false, "Enable web server mode (i.e., deliver resulting image over the web)")
flag.StringVar(&port, "port", "8080", "listening port when server_mode == true")
flag.BoolVar(&randomize, "random", false, "Use a random value for the radius parameter")
flag.Parse()

Gray = make([]color.Color, 255 * 3)
if randomize {
randomize_params()
}

Gray = make([]color.Color, 255*3)
for i := 0; i < 255*3; i++ {
Gray[i] = color.RGBA{uint8(i / 3), uint8((i+1) / 3), uint8((i+2) / 3), 255}
Gray[i] = color.RGBA{uint8(i / 3), uint8((i + 1) / 3), uint8((i + 2) / 3), 255}
}

Alternate = make([]color.Color, 20)
Expand All @@ -57,10 +72,10 @@ func init() {
Alternate[i] = color.RGBA{0xea, 0xfb, 0xc5, 255}
}
}

BlackWhite = make([]color.Color, 0)
for i := 0; i < 20; i++ {
if i % 2 == 0 {
if i%2 == 0 {
BlackWhite = append(BlackWhite, color.RGBA{0, 0, 0, 255})
} else {
BlackWhite = append(BlackWhite, color.RGBA{255, 255, 255, 255})
Expand All @@ -72,15 +87,15 @@ func it(ca, cb float64) (int, float64) {
var a, b float64 = 0, 0
for i := 0; i < IT; i++ {
as, bs := a*a, b*b
if as + bs > 4 {
if as+bs > 4 {
return i, as + bs
}
//if as + bs < .00001 {
// return .00001
//}
a, b = as - bs + ca, 2 * a * b + cb
a, b = as-bs+ca, 2*a*b+cb
}
return IT, a * a + b * b
return IT, a*a + b*b
}

var Gameboy = []color.Color{
Expand Down Expand Up @@ -111,7 +126,8 @@ var Retro = []color.Color{

var Gray, Alternate, BlackWhite []color.Color

func main() {
func create_image() img.Image {

width, height := xres*aa, yres*aa
ratio := float64(height) / float64(width)
//xpos, ypos, zoom_width := -.16070135, 1.0375665, 1.0e-7
Expand All @@ -121,9 +137,9 @@ func main() {
//xpos, ypos, zoom_width := .232223859135, .559654166164, .00000000004
y_radius := float64(radius * ratio)

temp_radius, temp_y_radius := radius / 4.0, y_radius / 4.0
temp_radius, temp_y_radius := radius/4.0, y_radius/4.0
for _, command := range focusstring {
switch(string(command)) {
switch string(command) {
case "1":
xpos -= temp_radius
ypos += temp_radius
Expand All @@ -145,25 +161,23 @@ func main() {
case "d":
xpos += temp_radius
case "r":
temp_radius, temp_y_radius = radius / 4, y_radius / 4
temp_radius, temp_y_radius = radius/4, y_radius/4
case "z":
radius /= 2
y_radius /= 2
temp_radius, temp_y_radius = radius / 4, y_radius / 4
temp_radius, temp_y_radius = radius/4, y_radius/4
default:
return
return nil
}
temp_radius /= 2
temp_y_radius /= 2
}


xmin, xmax := xpos - radius / 2.0, xpos + radius / 2.0
ymin, ymax := ypos - y_radius / 2.0, ypos + y_radius / 2.0
xmin, xmax := xpos-radius/2.0, xpos+radius/2.0
ymin, ymax := ypos-y_radius/2.0, ypos+y_radius/2.0

single_values := make([]float64, width*height)


single_values := make([]float64, width * height)

fmt.Print("Mandelling...")

var wg sync.WaitGroup
Expand All @@ -173,11 +187,11 @@ func main() {
go func(y int) {
defer wg.Done()
for x := 0; x < width; x++ {
a := (float64(x) / float64(width)) * (xmax - xmin) + xmin
b := (float64(y) / float64(height)) * (ymin - ymax) + ymax
a := (float64(x)/float64(width))*(xmax-xmin) + xmin
b := (float64(y)/float64(height))*(ymin-ymax) + ymax
stop_it, norm := it(a, b)
smooth_val := float64(IT - stop_it) + math.Log(norm)
i := y * width + x
smooth_val := float64(IT-stop_it) + math.Log(norm)
i := y*width + x
if invert {
single_values[i] = smooth_val
} else {
Expand All @@ -194,17 +208,15 @@ func main() {
sorted_values[i] = single_values[i]
}
sort.Float64s(sorted_values)

fmt.Println("Done")

fmt.Println("Done")

cont := make([]color.Color, 10000)
for i, _ := range cont {
//val := float64(i) / float64(len(cont))
val := i * 256 / len(cont)
val := i * 256 / len(cont)
cont[i] = color.RGBA{uint8(val), 0, uint8(255 - val), uint8(255)}
}


var pal []color.Color
palette_map := make(map[string][]color.Color)
Expand Down Expand Up @@ -245,12 +257,77 @@ func main() {
}
fmt.Println("Done")

fmt.Println("Resizing...")
fmt.Println("Resizing...")
image_resized := resize.Resize(uint(xres), uint(yres), image, resize.Lanczos3)
fmt.Println("Done")

out_file, _ := os.Create(out_filename)
png.Encode(out_file, image_resized)
fmt.Println("Finished writing to:", out_filename)
fmt.Printf("--r %v --x %v --y %v\n", radius, (xmin + xmax) / 2, (ymin + ymax) / 2)
return image_resized

}

func handler(w http.ResponseWriter, r *http.Request) {

// Overide (if present) global vars from query parameters
params := r.URL.Query()
for k, v := range params {
switch k {
// TODO: add error handling code
case "IT":
IT, _ = strconv.Atoi(v[0])
case "xres":
xres, _ = strconv.Atoi(v[0])
case "yres":
yres, _ = strconv.Atoi(v[0])
case "aa":
aa, _ = strconv.Atoi(v[0])
// // case "xpos":
// // xpos, _ = strconv.ParseFloat(v[0], 64)
// // case "ypos":
// // ypos, _ = strconv.ParseFloat(v[0], 64)
// case "radius":
// radius, _ = strconv.ParseFloat(v[0], 64)
case "palette":
palette_string = v[0]
case "focus":
focusstring = v[0]
case "invert":
invert, _ = strconv.ParseBool(v[0])
// case "random":
// randomize, _ = strconv.ParseBool(v[0])
// if randomize {
// randomize_params()
// }
}
}

randomize_params()
image_resized := create_image()
png.Encode(w, image_resized)

}

func randomize_params() {
radius = RADIUS_MIN + rand.Float64()*(RADIUS_MAX-RADIUS_MIN)
// radius = rand.Float64() * RADIUS_MAX
xpos = rand.Float64()
ypos = rand.Float64()
}

func main() {

if server_mode {

http.HandleFunc("/", handler)
log.Printf("Serving fractals on port %v\n", port)
addr := fmt.Sprintf("0.0.0.0:%s", port)
log.Fatal(http.ListenAndServe(addr, nil))

} else {
image_resized := create_image()
out_file, _ := os.Create(out_filename)
png.Encode(out_file, image_resized)
fmt.Println("Finished writing to:", out_filename)
// fmt.Printf("--r %v --x %v --y %v\n", radius, (xmin+xmax)/2, (ymin+ymax)/2)

}
}
Binary file removed object1.png
Binary file not shown.
Binary file removed spiral.jpg
Binary file not shown.
Binary file removed spiral.png
Binary file not shown.