Add mandelbrot Golang with webassembly
This commit is contained in:
241
mandelbrot-webassembly/server/main.go
Normal file
241
mandelbrot-webassembly/server/main.go
Normal file
@@ -0,0 +1,241 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
"syscall/js"
|
||||
)
|
||||
|
||||
type selection struct {
|
||||
startX int
|
||||
startY int
|
||||
endX int
|
||||
endY int
|
||||
}
|
||||
|
||||
type mbRender struct {
|
||||
doc js.Value
|
||||
ctx js.Value
|
||||
|
||||
backCtx []js.Value
|
||||
curCtx int
|
||||
|
||||
width float64
|
||||
height float64
|
||||
minX float64
|
||||
minY float64
|
||||
maxX float64
|
||||
maxY float64
|
||||
|
||||
colorMode string
|
||||
|
||||
imageData js.Value
|
||||
mandelbrot chan Mandelbrot
|
||||
mandelbrotValue Mandelbrot
|
||||
mandelbrotProgress chan float64
|
||||
mandelbrotProgressValue float64
|
||||
|
||||
mouseDown bool
|
||||
hasSelection bool
|
||||
selection selection
|
||||
}
|
||||
|
||||
type Mandelbrot []byte
|
||||
|
||||
func (mb *mbRender) getMandelbrot(maxIterations uint) {
|
||||
go func() {
|
||||
mb.mandelbrot <- mb.CalculateMandelbrot(maxIterations)
|
||||
}()
|
||||
}
|
||||
|
||||
func (mb *mbRender) start() {
|
||||
mb.doc = js.Global().Get("document")
|
||||
canvasEl := mb.doc.Call("getElementById", "canvas")
|
||||
|
||||
//mb.width = mb.doc.Get("body").Get("clientWidth").Float()
|
||||
//mb.height = mb.doc.Get("body").Get("clientHeight").Float()
|
||||
mb.width = 1000.0
|
||||
mb.height = 1000.0
|
||||
|
||||
mb.minX = -2.0
|
||||
mb.minY = -2.0
|
||||
mb.maxX = 2
|
||||
mb.maxY = 2
|
||||
|
||||
canvasEl.Set("width", mb.width)
|
||||
canvasEl.Set("height", mb.height)
|
||||
|
||||
mb.ctx = canvasEl.Call("getContext", "2d")
|
||||
mb.imageData = mb.ctx.Call("getImageData", 0, 0, mb.width, mb.height)
|
||||
mb.mandelbrot = make(chan Mandelbrot)
|
||||
mb.getMandelbrot(1000)
|
||||
|
||||
mouseDownEvt := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
|
||||
evt := args[0]
|
||||
mb.mouseDown = true
|
||||
mb.hasSelection = true
|
||||
mb.selection = selection{
|
||||
startX: evt.Get("clientX").Int() - canvasEl.Get("offsetLeft").Int(),
|
||||
startY: evt.Get("clientY").Int() - canvasEl.Get("offsetTop").Int(),
|
||||
endX: evt.Get("clientX").Int() - canvasEl.Get("offsetLeft").Int(),
|
||||
endY: evt.Get("clientY").Int() - canvasEl.Get("offsetTop").Int(),
|
||||
}
|
||||
return nil
|
||||
})
|
||||
defer mouseDownEvt.Release()
|
||||
|
||||
mouseMoveEvt := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
|
||||
evt := args[0]
|
||||
if mb.mouseDown {
|
||||
posX := evt.Get("clientX").Int() - canvasEl.Get("offsetLeft").Int()
|
||||
posY := evt.Get("clientY").Int() - canvasEl.Get("offsetTop").Int()
|
||||
dx := posX - mb.selection.startX
|
||||
dy := posY - mb.selection.startY
|
||||
if math.Abs(float64(dx)) > math.Abs(float64(dy)) {
|
||||
mb.selection.endX = mb.selection.startX + dx
|
||||
mb.selection.endY = mb.selection.startY + dx
|
||||
} else {
|
||||
mb.selection.endX = mb.selection.startX + dy
|
||||
mb.selection.endY = mb.selection.startY + dy
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
defer mouseMoveEvt.Release()
|
||||
|
||||
mouseUpEvt := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
|
||||
mb.mouseDown = false
|
||||
mb.hasSelection = false
|
||||
|
||||
newMinX := CalcReal(float64(mb.selection.startX), mb.width, mb.minX, mb.maxX)
|
||||
newMinY := CalcReal(float64(mb.selection.startY), mb.height, mb.minY, mb.maxY)
|
||||
newMaxX := CalcReal(float64(mb.selection.endX), mb.width, mb.minX, mb.maxX)
|
||||
newMaxY := CalcReal(float64(mb.selection.endY), mb.height, mb.minY, mb.maxY)
|
||||
|
||||
mb.minX = newMinX
|
||||
mb.minY = newMinY
|
||||
mb.maxX = newMaxX
|
||||
mb.maxY = newMaxY
|
||||
|
||||
mb.getMandelbrot(uint(math.Min(1000*1/math.Sqrt((mb.maxX-mb.minX)), 100000)))
|
||||
|
||||
return nil
|
||||
})
|
||||
defer mouseUpEvt.Release()
|
||||
|
||||
canvasEl.Call("addEventListener", "mousedown", mouseDownEvt)
|
||||
canvasEl.Call("addEventListener", "mousemove", mouseMoveEvt)
|
||||
canvasEl.Call("addEventListener", "mouseup", mouseUpEvt)
|
||||
|
||||
var renderFrame js.Func
|
||||
|
||||
renderFrame = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
|
||||
// mb.ctx.Call("clearRect", 0, 0, mb.width, mb.height)
|
||||
|
||||
select {
|
||||
case x, ok := <-mb.mandelbrot:
|
||||
if ok {
|
||||
mb.mandelbrotValue = x
|
||||
}
|
||||
default:
|
||||
}
|
||||
select {
|
||||
case x, ok := <-mb.mandelbrotProgress:
|
||||
if ok {
|
||||
mb.mandelbrotProgressValue = x
|
||||
}
|
||||
default:
|
||||
}
|
||||
|
||||
value := js.Global().Get("Uint8Array").New(len(mb.mandelbrotValue))
|
||||
js.CopyBytesToJS(value, mb.mandelbrotValue)
|
||||
mb.imageData.Get("data").Call("set", value)
|
||||
mb.ctx.Call("putImageData", mb.imageData, 0, 0)
|
||||
|
||||
if mb.hasSelection {
|
||||
mb.ctx.Set("fillStyle", "rgba(0, 0, 255, 0.8)")
|
||||
mb.ctx.Call("fillRect", mb.selection.startX, mb.selection.startY, mb.selection.endX-mb.selection.startX, mb.selection.endY-mb.selection.startY)
|
||||
}
|
||||
|
||||
js.Global().Call("requestAnimationFrame", renderFrame)
|
||||
return nil
|
||||
})
|
||||
|
||||
done := make(chan struct{}, 0)
|
||||
|
||||
js.Global().Call("requestAnimationFrame", renderFrame)
|
||||
<-done
|
||||
}
|
||||
|
||||
func (m Mandelbrot) SetPixel(width int, x int, y int, color []byte) {
|
||||
start := y*int(width)*4 + x*4
|
||||
for i := 0; i < 4; i++ {
|
||||
m[start+i] = color[i]
|
||||
}
|
||||
}
|
||||
|
||||
func (mb mbRender) GetColor(iteration uint, maxIterations uint) []byte {
|
||||
|
||||
intensity := (math.Pi / 2.0) * float64(iteration) / float64(maxIterations)
|
||||
|
||||
return []byte{
|
||||
byte(math.Sin(intensity) * 256),
|
||||
byte(math.Sin(intensity*2) * 256),
|
||||
byte(math.Cos(intensity) * 256),
|
||||
255,
|
||||
}
|
||||
|
||||
// HSL - red
|
||||
/*c := colorful.Hsl(float64(iteration)/256, 1.0, float64(iteration)/(float64(iteration)+8.0))
|
||||
return []byte{
|
||||
byte(c.R * 255),
|
||||
byte(c.G * 255),
|
||||
byte(c.B * 255),
|
||||
255,
|
||||
}*/
|
||||
}
|
||||
|
||||
func CalcReal(x, width, minX, maxX float64) float64 {
|
||||
return (x*(maxX-minX))/width + minX
|
||||
}
|
||||
func CalcImaginary(y, height, minY, maxY float64) float64 {
|
||||
return (y*(maxY-minY))/height + minY
|
||||
}
|
||||
|
||||
func (mb *mbRender) CalculateMandelbrot(maxIterations uint) Mandelbrot {
|
||||
fmt.Println("max iterations", maxIterations)
|
||||
mandelbrot := make(Mandelbrot, int(mb.width*mb.height*4))
|
||||
mb.mandelbrotProgress = make(chan float64)
|
||||
|
||||
for row := 0; row < int(mb.height); row++ {
|
||||
for col := 0; col < int(mb.width); col++ {
|
||||
cRe := CalcReal(float64(col), mb.width, mb.minX, mb.maxX)
|
||||
cIm := CalcImaginary(float64(row), mb.height, mb.minY, mb.maxY)
|
||||
|
||||
var x float64
|
||||
var y float64
|
||||
|
||||
var iteration uint
|
||||
for ; x*x+y*y <= 4 && iteration < maxIterations; iteration++ {
|
||||
newX := x*x - y*y + cRe
|
||||
y = 2*x*y + cIm
|
||||
x = newX
|
||||
}
|
||||
|
||||
mandelbrot.SetPixel(int(mb.width), col, row, mb.GetColor(iteration, maxIterations))
|
||||
}
|
||||
go func() {
|
||||
mb.mandelbrotProgress <- float64(row) / mb.height
|
||||
}()
|
||||
}
|
||||
|
||||
return mandelbrot
|
||||
}
|
||||
|
||||
func main() {
|
||||
fmt.Println("Hallo fögel")
|
||||
|
||||
mb := mbRender{}
|
||||
mb.start()
|
||||
}
|
Reference in New Issue
Block a user