applets/examples/ripple/ripple.go

124 lines
2.3 KiB
Go

package main
import (
"image"
"git.kirsle.net/go/render"
)
// App holds the ripple applet state.
type App struct {
e render.Engine
image image.Image
source *image.RGBA
data *image.RGBA
tex render.Texturer
// computed
width int
height int
size int
// frame buffers
buffer0 []int
buffer1 []int
}
// New creates the ripple applet.
func New(e render.Engine, i image.Image) *App {
var (
bounds = i.Bounds()
width = bounds.Max.X
height = bounds.Max.Y
size = width * height
)
a := &App{
e: e,
image: i,
source: render.ImageToRGBA(i),
data: render.ImageToRGBA(i),
width: width,
height: height,
size: size,
buffer0: make([]int, size),
buffer1: make([]int, size),
}
tex, err := a.e.StoreTexture("pic", a.data)
if err != nil {
panic(err)
}
a.tex = tex
return a
}
// Disturb creates a disturbance in the water.
func (a *App) Disturb(x, y, z int) {
if x < 2 || x > Width-2 || y < 1 || y > Height-2 {
return
}
var i = x + y*Width
a.buffer0[i] += z
a.buffer0[i-1] -= z
}
// Ripple runs the ripple algorithm.
func (a *App) Ripple() {
var (
width = a.width
size = a.size
)
// average cells to make the surface more even
for i := width + 1; i < size-width-1; i += 2 {
for x := 1; x < width-1; x++ {
avg := (a.buffer0[i] + a.buffer0[i+1] + a.buffer0[i-1] + a.buffer0[i-width] + a.buffer0[i+width]) / 5
a.buffer0[i] = avg
i++
}
}
// Loop for every non-edge element.
y := 0
for i := width + 1; i < size-width-1; i += 2 {
y++
for x := 1; x < width-1; x++ {
// Wave propagation
waveHeight := (a.buffer0[i-1]+
a.buffer0[i+1]+
a.buffer0[i+Width]+
a.buffer0[i-Width])/2 - a.buffer1[i]
a.buffer1[i] = waveHeight
// Some very fake lighting
var light = int(float64(waveHeight)*2.0 - float64(a.buffer1[i-2])*0.6)
if light < -10 {
light = -10
} else if light > 100 {
light = 100
}
color := a.source.At(x, y)
a.data.Set(x, y, render.FromColor(color).Lighten(light).ToColor())
i++
}
}
aux := a.buffer0
a.buffer0 = a.buffer1
a.buffer1 = aux
a.tex, _ = a.e.StoreTexture("pic", a.data)
}
// Present the visual state.
func (a *App) Present() {
rect := render.Rect{
W: a.width,
H: a.height,
}
a.e.Copy(a.tex, rect, rect)
a.e.Present()
}