forked from go/golangs_learn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
124 lines
2.9 KiB
124 lines
2.9 KiB
3 years ago
|
package fractal
|
||
|
|
||
|
import (
|
||
|
"image/color"
|
||
|
"math"
|
||
|
|
||
|
"fyne.io/fyne/v2"
|
||
|
"fyne.io/fyne/v2/canvas"
|
||
|
"fyne.io/fyne/v2/container"
|
||
|
"fyne.io/fyne/v2/theme"
|
||
|
)
|
||
|
|
||
|
type fractal struct {
|
||
|
currIterations uint
|
||
|
currScale, currX, currY float64
|
||
|
|
||
|
window fyne.Window
|
||
|
canvas fyne.CanvasObject
|
||
|
}
|
||
|
|
||
|
func (f *fractal) Layout(objects []fyne.CanvasObject, size fyne.Size) {
|
||
|
f.canvas.Resize(size)
|
||
|
}
|
||
|
|
||
|
func (f *fractal) MinSize(objects []fyne.CanvasObject) fyne.Size {
|
||
|
return fyne.NewSize(320, 240)
|
||
|
}
|
||
|
|
||
|
//lint:ignore U1000 See TODO inside the .Show() method.
|
||
|
func (f *fractal) refresh() {
|
||
|
if f.currScale >= 1.0 {
|
||
|
f.currIterations = 100
|
||
|
} else {
|
||
|
f.currIterations = uint(100 * (1 + math.Pow((math.Log10(1/f.currScale)), 1.25)))
|
||
|
}
|
||
|
|
||
|
f.window.Canvas().Refresh(f.canvas)
|
||
|
}
|
||
|
|
||
|
func (f *fractal) scaleChannel(c float64, start, end uint32) uint8 {
|
||
|
if end >= start {
|
||
|
return (uint8)(c*float64(uint8(end-start))) + uint8(start)
|
||
|
}
|
||
|
|
||
|
return (uint8)((1-c)*float64(uint8(start-end))) + uint8(end)
|
||
|
}
|
||
|
|
||
|
func (f *fractal) scaleColor(c float64, start, end color.Color) color.Color {
|
||
|
r1, g1, b1, _ := start.RGBA()
|
||
|
r2, g2, b2, _ := end.RGBA()
|
||
|
return color.RGBA{f.scaleChannel(c, r1, r2), f.scaleChannel(c, g1, g2), f.scaleChannel(c, b1, b2), 0xff}
|
||
|
}
|
||
|
|
||
|
func (f *fractal) mandelbrot(px, py, w, h int) color.Color {
|
||
|
drawScale := 3.5 * f.currScale
|
||
|
aspect := (float64(h) / float64(w))
|
||
|
cRe := ((float64(px)/float64(w))-0.5)*drawScale + f.currX
|
||
|
cIm := ((float64(py)/float64(w))-(0.5*aspect))*drawScale - f.currY
|
||
|
|
||
|
var i uint
|
||
|
var x, y, xsq, ysq float64
|
||
|
|
||
|
for i = 0; i < f.currIterations && (xsq+ysq <= 4); i++ {
|
||
|
xNew := float64(xsq-ysq) + cRe
|
||
|
y = 2*x*y + cIm
|
||
|
x = xNew
|
||
|
|
||
|
xsq = x * x
|
||
|
ysq = y * y
|
||
|
}
|
||
|
|
||
|
if i == f.currIterations {
|
||
|
return theme.BackgroundColor()
|
||
|
}
|
||
|
|
||
|
mu := (float64(i) / float64(f.currIterations))
|
||
|
c := math.Sin((mu / 2) * math.Pi)
|
||
|
|
||
|
return f.scaleColor(c, theme.PrimaryColor(), theme.ForegroundColor())
|
||
|
}
|
||
|
|
||
|
//lint:ignore U1000 See TODO inside the .Show() method.
|
||
|
func (f *fractal) fractalRune(r rune) {
|
||
|
if r == '+' {
|
||
|
f.currScale /= 1.1
|
||
|
} else if r == '-' {
|
||
|
f.currScale *= 1.1
|
||
|
}
|
||
|
|
||
|
f.refresh()
|
||
|
}
|
||
|
|
||
|
//lint:ignore U1000 See TODO inside the .Show() method.
|
||
|
func (f *fractal) fractalKey(ev *fyne.KeyEvent) {
|
||
|
delta := f.currScale * 0.2
|
||
|
if ev.Name == fyne.KeyUp {
|
||
|
f.currY -= delta
|
||
|
} else if ev.Name == fyne.KeyDown {
|
||
|
f.currY += delta
|
||
|
} else if ev.Name == fyne.KeyLeft {
|
||
|
f.currX += delta
|
||
|
} else if ev.Name == fyne.KeyRight {
|
||
|
f.currX -= delta
|
||
|
}
|
||
|
|
||
|
f.refresh()
|
||
|
}
|
||
|
|
||
|
// Show loads a Mandelbrot fractal example window for the specified app context
|
||
|
func Show(win fyne.Window) fyne.CanvasObject {
|
||
|
fractal := &fractal{window: win}
|
||
|
fractal.canvas = canvas.NewRasterWithPixels(fractal.mandelbrot)
|
||
|
|
||
|
fractal.currIterations = 100
|
||
|
fractal.currScale = 1.0
|
||
|
fractal.currX = -0.75
|
||
|
fractal.currY = 0.0
|
||
|
|
||
|
return container.New(fractal, fractal.canvas)
|
||
|
// TODO: Register, and unregister, these keys:
|
||
|
//window.Canvas().SetOnTypedRune(fractal.fractalRune)
|
||
|
//window.Canvas().SetOnTypedKey(fractal.fractalKey)
|
||
|
}
|