Skip to content

Commit

Permalink
Initial
Browse files Browse the repository at this point in the history
  • Loading branch information
PashaWNN committed Sep 18, 2019
0 parents commit dc88916
Show file tree
Hide file tree
Showing 8 changed files with 727 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
main.wasm
.idea
Empty file added .nojekyll
Empty file.
2 changes: 2 additions & 0 deletions Caddyfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
0.0.0.0:8080
mime .wasm application/wasm
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Boids simulation

Boids algorithm implementation in Go language, compiled to WebAssembly

[Live demo](pashawnn.github.io/boids_go/)


## Compilation

```
GOOS=js GOARCH=wasm go build -o main.wasm
```

Compiled binary can be found in `Releases` section of GitHub repository. Note that you can't just open index.html from local filesystem. You need web-server which sets correct mime type (`mime .wasm application/wasm`) to run WebAssembly. Simpliest solution is to download Caddy server and just run:
```
caddy
```
from project root.
145 changes: 145 additions & 0 deletions boids.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package main

import (
"github.com/go-gl/mathgl/mgl32"
"math"
"math/rand"
)


func dist(p, q mgl32.Vec2) float32 {
return float32(math.Sqrt(math.Pow(float64(q[0] - p[0]), 2) + math.Pow(float64(q[1] - p[1]), 2)))
}

func limit(p mgl32.Vec2, lim float32) mgl32.Vec2{
if p[0] > lim {
p[0] = lim
} else if p[0] < -lim {
p[0] = -lim
}
if p[1] > lim {
p[1] = lim
} else if p[1] < -lim {
p[1] = -lim
}
return p
}


var alignmentPerception = float32(20.0)
var cohesionPerception = float32(40.0)
var divisionPerception = float32(20.0)
var alignmentCoef = float32(1.0)
var cohesionCoef = float32(1.0)
var divisionCoef = float32(1.0)
var maxSpeed = float32(4)
var maxForce = float32(1)
var avg mgl32.Vec2
var total int


type Boid struct{
siblings *[]Boid
position mgl32.Vec2
velocity mgl32.Vec2
acceleration mgl32.Vec2
}


func NewBoid(x, y float32, boids *[]Boid) Boid {
b := Boid{
siblings: boids,
position:mgl32.Vec2{x, y},
velocity:mgl32.Vec2{(rand.Float32() * 3) - 1.5, (rand.Float32() * 3) - 1.5},
acceleration:mgl32.Vec2{0,0},
}
return b
}


func (boid* Boid) alignment() mgl32.Vec2 {
avg = mgl32.Vec2{0,0}
total = 0

for _, sibling := range *boid.siblings {
if (sibling != *boid) && dist(boid.position, sibling.position) < alignmentPerception {
avg = avg.Add(sibling.velocity)
total++
}
}
if total > 0 {
avg = avg.Mul(1.0 / float32(total) * alignmentCoef)
avg = avg.Normalize().Mul(maxSpeed)
avg = avg.Sub(boid.velocity)
avg = limit(avg, maxForce)
}
return avg
}


func (boid* Boid) cohesion() mgl32.Vec2 {
avg = mgl32.Vec2{0,0}
total = 0

for _, sibling := range *boid.siblings {
if (sibling != *boid) && dist(boid.position, sibling.position) < cohesionPerception {
avg = avg.Add(sibling.position)
total++
}
}
if total > 0 {
avg = avg.Mul(1.0 / float32(total) * cohesionCoef)
avg = avg.Sub(boid.position)
avg = avg.Normalize().Mul(maxSpeed)
avg = avg.Sub(boid.velocity)
avg = limit(avg, maxForce)
}
return avg
}


func (boid* Boid) divison() mgl32.Vec2 {
avg = mgl32.Vec2{0,0}
total = 0

for _, sibling := range *boid.siblings {
d := dist(boid.position, sibling.position)
if (sibling != *boid) && d < divisionPerception {
diff := boid.position.Sub(sibling.position).Mul(1/(d*d))
avg = avg.Add(diff)
total++
}
}
if total > 0 {
avg = avg.Mul(1.0 / float32(total) * divisionCoef)
avg = avg.Normalize().Mul(maxSpeed)
avg = avg.Sub(boid.velocity)
avg = limit(avg, maxForce)
}
return avg
}


func (boid* Boid) Tick() {
boid.acceleration = boid.acceleration.Add(boid.alignment().Mul(1.5))
boid.acceleration = boid.acceleration.Add(boid.cohesion().Mul(1.0))
boid.acceleration = boid.acceleration.Add(boid.divison().Mul(2.0))

boid.position = boid.position.Add(boid.velocity)
boid.velocity = boid.velocity.Add(boid.acceleration.Mul(0.5))
boid.velocity = limit(boid.velocity, maxSpeed)
boid.acceleration = boid.acceleration.Mul(0)

if float64(boid.position.X()) > width {
boid.position[0] = 0
} else if float64(boid.position.X()) < 0 {
boid.position[0] = float32(width)
}
if float64(boid.position.Y()) > height {
boid.position[1] = 0
} else if float64(boid.position.Y()) < 0 {
boid.position[1] = float32(height)
}


}
22 changes: 22 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<html>
<head>
<meta charset="utf-8" />
<title>Go WebAssembly - Boids</title>
<meta name="theme-color" content="#000000" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<style>body{height:100%;width:100%}</style>

<script type="text/javascript" src="./wasm_exec.js"></script>
<script>
const go = new Go();
WebAssembly.instantiateStreaming(fetch('main.wasm'),go.importObject).then( res=> {
go.run(res.instance)
})
</script>
<style>
</style>
</head>
<body>

</body>
</html>
73 changes: 73 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package main

import (
"github.com/llgcode/draw2d/draw2dimg"
"github.com/llgcode/draw2d/draw2dkit"
"github.com/markfarnan/go-canvas/canvas"
"image/color"
"math/rand"
)

var boids []Boid

var done chan struct{}

var cvs *canvas.Canvas2d
var width float64
var height float64


func main() {
cvs, _ = canvas.NewCanvas2d(true)
height = float64(cvs.Height())
width = float64(cvs.Width())
for i := 0; i < 70; i++ {

boids = append(boids, NewBoid(float32(rand.Intn(500)), float32(rand.Intn(700)), &boids))
}
cvs.Start(60, Render)
<-done
}


func tick() {
for i, _ := range boids {
boids[i].Tick()
}
}

func drawBoid(gc *draw2dimg.GraphicContext, b Boid) {
gc.BeginPath()
draw2dkit.Circle(gc, float64(b.position.X()), float64(b.position.Y()), 5)
gc.FillStroke()
gc.Close()

x, y := float64(b.position[0]), float64(b.position[1])
sec := b.position.Sub(b.velocity.Normalize().Mul(10))
tX, tY := float64(sec[0]), float64(sec[1])
gc.BeginPath()
gc.MoveTo(x, y)
gc.LineTo(tX, tY)
gc.Stroke()
gc.Close()

//gc.BeginPath()
//draw2dkit.Circle(gc, float64(b.position.X()), float64(b.position.Y()), 20)
//gc.Stroke()
//gc.Close()
}

func Render(gc *draw2dimg.GraphicContext) bool {
tick()
gc.SetFillColor(color.RGBA{0xff, 0xff, 0xff, 0xff})
gc.Clear()

gc.SetFillColor(color.RGBA{0xff, 0x00, 0x00, 0xff})
gc.SetStrokeColor(color.RGBA{0xff, 0x00, 0x00, 0xff})

for _, boid := range boids {
drawBoid(gc, boid)
}

return true
}
Loading

0 comments on commit dc88916

Please sign in to comment.