From 885373889b9f7e90021bf213ebfb633ca5ccff0c Mon Sep 17 00:00:00 2001 From: xoy Date: Tue, 22 Oct 2024 20:34:39 +0200 Subject: [PATCH] [First commit] --- .gitignore | 1 + config.go | 13 +++++ functions.go | 19 ++++++++ go.mod | 14 ++++++ go.sum | 16 +++++++ main.go | 133 +++++++++++++++++++++++++++++++++++++++++++++++++++ readme.md | 10 ++++ types.go | 55 +++++++++++++++++++++ utility.go | 13 +++++ 9 files changed, 274 insertions(+) create mode 100644 .gitignore create mode 100644 config.go create mode 100644 functions.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go create mode 100644 readme.md create mode 100644 types.go create mode 100644 utility.go 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) + } +}