commit 885373889b9f7e90021bf213ebfb633ca5ccff0c Author: xoy Date: Tue Oct 22 20:34:39 2024 +0200 [First commit] diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ac3cbde --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +img/* diff --git a/config.go b/config.go new file mode 100644 index 0000000..2dc8cbb --- /dev/null +++ b/config.go @@ -0,0 +1,13 @@ +package main + +const ( + screenWidth int = 500 + screenHeight int = 500 + windowWidth int = 1000 + windowHeight int = 1000 + layerCount int = 3 +) + +var ( + function Function = &X1{} +) diff --git a/functions.go b/functions.go new file mode 100644 index 0000000..c415627 --- /dev/null +++ b/functions.go @@ -0,0 +1,19 @@ +package main + +import ( + "math" +) + +type X1 struct{} + +func (x *X1) Func(f float64) uint16 { + v := (65535 / 2) * (float64(screenHeight+screenWidth) / 2) / f + return ToUint16(v) +} + +type X2 struct{} + +func (x *X2) Func(f float64) uint16 { + v := (math.Sin((float64(screenWidth)*float64(screenHeight))/f) + 1) / 2 * 65535 + return ToUint16(v) +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..9de5a65 --- /dev/null +++ b/go.mod @@ -0,0 +1,14 @@ +module git.ctdo.de/xoy/xvisuals + +go 1.23.2 + +require github.com/hajimehoshi/ebiten/v2 v2.8.1 + +require ( + github.com/ebitengine/gomobile v0.0.0-20240911145611-4856209ac325 // indirect + github.com/ebitengine/hideconsole v1.0.0 // indirect + github.com/ebitengine/purego v0.8.0 // indirect + github.com/jezek/xgb v1.1.1 // indirect + golang.org/x/sync v0.8.0 // indirect + golang.org/x/sys v0.25.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..8ba398b --- /dev/null +++ b/go.sum @@ -0,0 +1,16 @@ +github.com/ebitengine/gomobile v0.0.0-20240911145611-4856209ac325 h1:Gk1XUEttOk0/hb6Tq3WkmutWa0ZLhNn/6fc6XZpM7tM= +github.com/ebitengine/gomobile v0.0.0-20240911145611-4856209ac325/go.mod h1:ulhSQcbPioQrallSuIzF8l1NKQoD7xmMZc5NxzibUMY= +github.com/ebitengine/hideconsole v1.0.0 h1:5J4U0kXF+pv/DhiXt5/lTz0eO5ogJ1iXb8Yj1yReDqE= +github.com/ebitengine/hideconsole v1.0.0/go.mod h1:hTTBTvVYWKBuxPr7peweneWdkUwEuHuB3C1R/ielR1A= +github.com/ebitengine/purego v0.8.0 h1:JbqvnEzRvPpxhCJzJJ2y0RbiZ8nyjccVUrSM3q+GvvE= +github.com/ebitengine/purego v0.8.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +github.com/hajimehoshi/ebiten/v2 v2.8.1 h1:6n6ZXnbeSCZccdqrH7s9Ut+dll9TEostUqbc72Tis/g= +github.com/hajimehoshi/ebiten/v2 v2.8.1/go.mod h1:SXx/whkvpfsavGo6lvZykprerakl+8Uo1X8d2U5aAnA= +github.com/jezek/xgb v1.1.1 h1:bE/r8ZZtSv7l9gk6nU0mYx51aXrvnyb44892TwSaqS4= +github.com/jezek/xgb v1.1.1/go.mod h1:nrhwO0FX/enq75I7Y7G8iN1ubpSGZEiA3v9e9GyRFlk= +golang.org/x/image v0.20.0 h1:7cVCUjQwfL18gyBJOmYvptfSHS8Fb3YUDtfLIZ7Nbpw= +golang.org/x/image v0.20.0/go.mod h1:0a88To4CYVBAHp5FXJm8o7QbUl37Vd85ply1vyD8auM= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= diff --git a/main.go b/main.go new file mode 100644 index 0000000..62f1fd2 --- /dev/null +++ b/main.go @@ -0,0 +1,133 @@ +package main + +import ( + "image/color" + "image/png" + "log" + "math" + "math/rand" + "os" + "strconv" + "time" + + "github.com/hajimehoshi/ebiten/v2" +) + +type Game struct { + points []*Position + preScreens []*ColorArray + screen *ebiten.Image + pressCountEnter int + pressCountSpace int +} + +func (g *Game) NewImage() { + image := NewColorArray() + for i := 0; i < 3; i++ { + x, y := rand.Intn(screenHeight), rand.Intn(screenWidth) + g.points[i] = &Position{x, y} + } + for x := 0; x < screenWidth; x++ { + for y := 0; y < screenHeight; y++ { + c := make([]uint16, 3) + for i := 0; i < 3; i++ { + p := g.points[i] + if p.X != x || p.Y != y { + v := math.Abs(p.Dist(&Position{x, y})) + c[i] = function.Func(v) + } else { + c[0] = 65535 + c[1] = 65535 + c[2] = 65535 + break + } + } + image.Set(x, y, color.RGBA64{c[0], c[1], c[2], 255}) + } + } + g.preScreens = append(g.preScreens, image) +} + +func (gm *Game) Init() { + for i := 0; i < layerCount; i++ { + gm.NewImage() + } + + for x := 0; x < screenWidth; x++ { + for y := 0; y < screenHeight; y++ { + var r, g, b int64 = 0, 0, 0 + for i := 0; i < layerCount; i++ { + R, G, B, _ := gm.preScreens[i].At(x, y).RGBA() + r += int64(R) + g += int64(G) + b += int64(B) + } + r /= int64(layerCount) + g /= int64(layerCount) + b /= int64(layerCount) + gm.screen.Set(x, y, color.RGBA64{uint16(r), uint16(g), uint16(b), 65535}) + } + } +} + +func (g *Game) ReInit() { + g.points = make([]*Position, 3) + g.preScreens = make([]*ColorArray, 0) + g.screen = ebiten.NewImage(screenWidth, screenHeight) + g.Init() +} + +func NewGame() *Game { + gm := &Game{ + make([]*Position, 3), + make([]*ColorArray, 0), + ebiten.NewImage(screenWidth, screenHeight), + 0, + 0, + } + gm.Init() + return gm +} + +func (g *Game) Update() error { + if ebiten.IsKeyPressed(ebiten.KeyEnter) && g.pressCountEnter == 0 { + file, err := os.Create(strconv.FormatInt(time.Now().Unix(), 10) + ".png") + if err != nil { + return err + } + defer file.Close() + err = png.Encode(file, g.screen) + if err != nil { + return err + } + + g.pressCountEnter++ + } else if !ebiten.IsKeyPressed(ebiten.KeyEnter) && g.pressCountEnter > 0 { + g.pressCountEnter = 0 + } + + if ebiten.IsKeyPressed(ebiten.KeySpace) && g.pressCountSpace == 0 { + g.ReInit() + g.pressCountSpace++ + } else if !ebiten.IsKeyPressed(ebiten.KeySpace) && g.pressCountSpace > 0 { + g.pressCountSpace = 0 + } + return nil +} + +func (g *Game) Draw(screen *ebiten.Image) { + screen.DrawImage(g.screen, nil) +} + +func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) { + return screenWidth, screenHeight +} + +func main() { + g := NewGame() + ebiten.SetWindowSize(windowWidth, windowHeight) + ebiten.SetWindowTitle("XVisuals") + if err := ebiten.RunGame(g); err != nil { + log.Fatal(err) + } +} diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..4d141eb --- /dev/null +++ b/readme.md @@ -0,0 +1,10 @@ +# XVisuals + +Visualization of mathematical formulas. + +## Controls + +| Key | Action | +| ----- | ------------------ | +| Space | Generate new image | +| Enter | Save current image | diff --git a/types.go b/types.go new file mode 100644 index 0000000..c86f9f6 --- /dev/null +++ b/types.go @@ -0,0 +1,55 @@ +package main + +import ( + "image/color" + "math" +) + +type Function interface { + Func(float64) uint16 +} + +type ColorArray struct { + img []color.Color +} + +func NewColorArray() *ColorArray { + ca := &ColorArray{make([]color.Color, screenWidth*screenHeight)} + ca.Clear(color.Black) + return ca +} + +func (ca *ColorArray) Clear(c color.Color) { + for i := 0; i < len(ca.img); i++ { + ca.img[i] = c + } +} + +func (ca *ColorArray) At(x, y int) color.Color { + position := x + (y * screenHeight) + if position > len(ca.img) || position < 0 { + return color.Black + } else { + return ca.img[position] + } +} + +func (ca *ColorArray) Set(x, y int, c color.Color) { + position := x + (y * screenHeight) + if position < len(ca.img) && position >= 0 { + ca.img[position] = c + } +} + +type Position struct { + X int + Y int +} + +func (p *Position) Length() float64 { + return math.Sqrt(float64(p.X)*float64(p.X) + float64(p.Y)*float64(p.Y)) +} + +func (p *Position) Dist(P *Position) float64 { + return (&Position{p.X - P.X, p.Y - P.Y}).Length() +} diff --git a/utility.go b/utility.go new file mode 100644 index 0000000..0b37ebb --- /dev/null +++ b/utility.go @@ -0,0 +1,13 @@ +package main + +import ( + "math" +) + +func ToUint16(f float64) uint16 { + if f > 65535 { + return uint16(math.Mod(f, 65535)) + } else { + return uint16(f) + } +}