-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
235 lines (197 loc) · 8.2 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
package main
import (
"flag"
"fmt"
"image"
"image/color"
"image/draw"
"image/jpeg"
"os"
"strings"
"sync"
)
const (
defaultGradient = `[["0.0", "000764"],["0.16", "026bcb"],["0.42", "edffff"],["0.6425", "ffaa00"],["0.8675", "000200"],["1.0","000764"]]`
)
const (
// Modes
imageMode = "image"
coordinatesMode = "coordsAt"
)
// Fractals supported
const (
mutantMandelbrotAlgoValue = "mutant_mandelbrot"
mandelbrotAlgoValue = "mandelbrot"
burningShipAlgoValue = "ship"
juliaAlgoValue = "julia"
z1ZcZIAlgoValue = "z1zczi"
boujeeAlgoValue = "boujee"
logTanAlgoValue = "logtan"
sharkFinAlgoValue = "sharkfin"
)
type config struct {
algorithm string // Which algorithm to use
maxIterations int // How many iterations to allow before giving up and treating as escaped
bailout float64 // Bailout point after which a point is considered to have escaped. Overriden for Julia
width int // Width in pixels of the output image
height int // Height in pixels of the output image
pointX int // X coordinate of a pixel being scaled to the complex plane
pointY int // Y coordinate of a pixel being scaled to the complex plane
midX float64 // Real component of the complex number being used as the centre of the plot
midY float64 // Imaginary component of the complex number being used as the centre of the plot
zoom float64 // Zoom level of the plot
output string // Path to output image to
filename string // Name of the output image
gradient string // Gradient to use for colouring. Ignored when using noColour mode
mode string // Render an image or calculate coordinates
colourMode string // Colour mode of the image
constR float64 // Real component of the constant in a Julia plot
constI float64 // Imaginary component of the constant in a Julia Plot
}
// A Plane represents the base confines of the complex plane for a fractal based
// an escape time function.
type Plane struct {
rMin float64 // The smallest value of the real component
rMax float64 // The largest value of the real component
iMin float64 // The smallest value of the imaginary component
iMax float64 // The largest value of the imaginary component
}
// A Point represents a set of coordinates in the complex plane,
// and the coresponding point in a bitmap
type Point struct {
X int // The X coordinated in the bitmap, where 0 is the left column
Y int // The Y coordinated in the bitmap, where 0 is the top line
real float64 // The scaled real component of the complex coordinate
imag float64 // The scaled real component of the complex coordinate
}
// A PlottedPoint represents the result of the escape time function
type PlottedPoint struct {
X int // The X coordinated in the bitmap, where 0 is the left column
Y int // The Y coordinated in the bitmap, where 0 is the top line
real float64 // The real component of final value of z in the escape time calculation
imag float64 // The imaginary component of final value of z in the escape time calculation
Iterations int // The number of iterations it took to determine a result
Escaped bool // True if the coordinate escaped the escape time function
}
func main() {
c := getConfig()
if c.algorithm == mandelbrotAlgoValue {
m := newMandelbrot()
m.process(c)
} else if c.algorithm == mutantMandelbrotAlgoValue {
m := newMutantMandelbrot()
m.process(c)
} else if c.algorithm == burningShipAlgoValue {
b := newBurningShip()
b.process(c)
} else if c.algorithm == juliaAlgoValue {
j := newJulia()
j.process(c)
} else if c.algorithm == z1ZcZIAlgoValue {
o := newZ1ZcZi()
o.process(c)
} else if c.algorithm == boujeeAlgoValue {
o := newboojee()
o.process(c)
} else if c.algorithm == logTanAlgoValue {
o := newLogTan()
o.process(c)
} else if c.algorithm == sharkFinAlgoValue {
o := newSharkFin()
o.process(c)
}
}
func getConfig() config {
var c config
var supportedAlgorithms = []string{mandelbrotAlgoValue, juliaAlgoValue, burningShipAlgoValue, mutantMandelbrotAlgoValue, z1ZcZIAlgoValue, boujeeAlgoValue, logTanAlgoValue, sharkFinAlgoValue}
var supportedColourings = []string{trueColouring, bandedColouring, smoothColouring, noColouring}
var supportedModes = []string{imageMode, coordinatesMode}
flag.StringVar(&c.algorithm, "a", "mandelbrot", "Fractal algorithm: "+strings.Join(supportedAlgorithms, ", "))
flag.Float64Var(&c.midX, "r", -99.0, "Real component of the midpoint.")
flag.Float64Var(&c.midY, "i", -99.0, "Imaginary component of the midpoint.")
flag.Float64Var(&c.zoom, "z", 1, "Zoom level.")
flag.StringVar(&c.output, "o", ".", "Output path.")
flag.StringVar(&c.filename, "f", "", "Output file name.")
flag.StringVar(&c.colourMode, "c", "none", "Colour mode: "+strings.Join(supportedColourings, ", "))
flag.Float64Var(&c.bailout, "b", 4.0, "Bailout value.")
flag.IntVar(&c.width, "w", 1600, "Width of render.")
flag.IntVar(&c.height, "h", 1600, "Height of render.")
flag.IntVar(&c.maxIterations, "m", 2000, "Maximum Iterations before giving up on finding an escape.")
flag.StringVar(&c.gradient, "g", defaultGradient, "Gradient to use.")
flag.StringVar(&c.mode, "mode", "image", "Mode: "+strings.Join(supportedModes, ", "))
flag.IntVar(&c.pointX, "x", 0, "x cordinate of a pixel, used for translating to the real component. 0,0 is top left.")
flag.IntVar(&c.pointY, "y", 0, "y cordinate of a pixel, used for translating to the real component. 0,0 is top left.")
flag.Float64Var(&c.constR, "cr", 0.0, "Real component of the const point in a Julia set.")
flag.Float64Var(&c.constI, "ci", 0.0, "Imaginary component of the const point in a Julia set.")
flag.Parse()
return c
}
func max(a float64, b float64) float64 {
if a > b {
return a
}
return b
}
func min(a float64, b float64) float64 {
if a > b {
return b
}
return a
}
func initialiseimage(c config) *image.NRGBA {
bounds := image.Rect(0, 0, c.width, c.height)
mbi := image.NewNRGBA(bounds)
draw.Draw(mbi, bounds, image.NewUniform(color.Black), image.ZP, draw.Src)
return mbi
}
func saveimage(mbi *image.NRGBA, filepath string, filename string) {
file, err := os.Create(filepath + "/" + filename)
if err != nil {
fmt.Println(err)
}
if err = jpeg.Encode(file, mbi, &jpeg.Options{Quality: jpeg.DefaultQuality}); err != nil {
fmt.Println(err)
}
if err = file.Close(); err != nil {
fmt.Println(err)
}
}
func (p *Plane) getScale(zoom float64, height int, width int) (float64, float64, float64) {
var pixelScaleRealAxis = (p.rMax - p.rMin) / float64(width-1) / zoom
var pixelScaleImagAxis = (p.iMax - p.iMin) / float64(height-1) / zoom
var pixelScale = min(pixelScaleRealAxis, pixelScaleImagAxis)
pixelOffsetReal := float64(width-1) / 2.0
pixelOffsetImag := float64(height-1) / 2.0
return pixelScale, pixelOffsetReal, pixelOffsetImag
}
func (p *Plane) calculateCoordinatesAtPoint(config config) (float64, float64) {
var pixelScale, pixelOffsetReal, pixelOffsetImag = p.getScale(config.zoom, config.height, config.width)
var real = config.midX + (float64(config.pointX)-pixelOffsetReal)*pixelScale
var imag = config.midY - pixelScale*(-1.0*float64(config.pointY)+pixelOffsetImag)
return real, imag
}
func (p *Plane) iterateOverPoints(config config, plottedChannel chan PlottedPoint, calc escapeCalculator) {
var pixelScale, pixelOffsetReal, pixelOffsetImag = p.getScale(config.zoom, config.height, config.width)
pointsChannel := make(chan Point)
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go func() {
for p := range pointsChannel {
var escaped, iteration, finalReal, finalImag = calc(p.real, p.imag, config)
plottedChannel <- PlottedPoint{p.X, p.Y, finalReal, finalImag, iteration, escaped}
}
wg.Done()
}()
}
for x := 0; x < config.width; x++ {
r := config.midX + (float64(x)-pixelOffsetReal)*pixelScale
for y := 0; y < config.height; y++ {
i := config.midY + pixelScale*(-1.0*float64(y)+pixelOffsetImag)
pointsChannel <- Point{x, y, r, i}
}
}
close(pointsChannel)
wg.Wait()
}
type escapeCalculator func(real float64, imag float64, config config) (escaped bool, iterations int, finalReal float64, finalImaginary float64)