- Download Go for your platform from here
- Create a new directory and switch to it
- Download sand.go into it
- Run
$ go mod init mainthis will create a file called go.mod that's needed for the next step - Run
$ go mod tidythis will pull down the packages needed to run the program - Run
$ go run sand.gohit ESC to quit, or just close the window
Last active
October 30, 2025 18:51
-
-
Save jphsd/fc9f07586ad277987e91ec503f34f5d3 to your computer and use it in GitHub Desktop.
Side scrolling landscape using glui and graphics2d packages
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package main | |
| import ( | |
| "flag" | |
| "github.com/jphsd/glui" | |
| "github.com/jphsd/graphics2d" | |
| "github.com/jphsd/graphics2d/color" | |
| "github.com/jphsd/graphics2d/image" | |
| "image/draw" | |
| "math" | |
| "math/rand" | |
| ) | |
| // From an idea by J. Tarbell | |
| // http://www.complexification.net/gallery/machines/sandstroke/ | |
| type Sand struct { | |
| width int | |
| height int | |
| grains int | |
| gage float64 | |
| r float64 | |
| sg float64 | |
| img *image.RGBA | |
| } | |
| // GLUI scrolling landscape | |
| func main() { | |
| rf := flag.Float64("r", 2, "point radius") | |
| gf := flag.Int("g", 200, "grains") | |
| flag.Parse() | |
| s := &Sand{width: 1000, height: 500, grains: *gf, gage: 200, r: *rf} | |
| win := glui.NewGLWin(s.width, s.height, "Sand", s.First(), true) | |
| glui.NewKeyListener(win, func(k, _ int, _ glui.Action, _ glui.ModifierKey) { | |
| if glui.GetKeyName(k) == "ESCAPE" { | |
| win.Win.SetShouldClose(true) | |
| } | |
| }) | |
| // Allow the system to render it | |
| glui.Loop(func() { | |
| img := s.Next() | |
| if img != nil { | |
| win.SetImage(img) | |
| } | |
| }) | |
| } | |
| func (s *Sand) First() *image.RGBA { | |
| s.sg = rand.Float64()*.09 + 0.01 // [0.01,0.1] | |
| s.img = image.NewRGBA(s.width, s.height, color.White) | |
| x, dx, y := 0.0, 1.0, float64(s.height)/2 | |
| for range s.width { | |
| s.sg += rand.Float64()*0.1 - 0.05 | |
| if s.sg < 0 { | |
| s.sg = 0 | |
| } else if s.sg > 1 { | |
| s.sg = 1 | |
| } | |
| w := s.sg / float64(s.grains-1) | |
| for i := range s.grains { | |
| a := 0.1 - float64(i)/float64(s.grains*10) | |
| ai := uint8(a * 256) | |
| //dy := s.gage * float64(i) * w | |
| //dy := s.gage * math.Sin(float64(i)*w) | |
| dy := s.gage * math.Sin(math.Sin(float64(i)*w)) | |
| if i > 0 { | |
| point(s.img, x, y+dy, s.r, color.RGBA{0, 0, 0, ai}) | |
| } | |
| point(s.img, x, y-dy, s.r, color.RGBA{0, 0, 0, ai}) | |
| } | |
| x += dx | |
| } | |
| return s.img | |
| } | |
| func (s *Sand) Next() *image.RGBA { | |
| bounds := s.img.Bounds() | |
| img := image.NewRGBA(s.width, s.height, color.Black) // Saves fill on creation | |
| // Blit old image and set remainder to white | |
| rect := image.Rect(bounds.Min.X, bounds.Min.Y, bounds.Max.X-1, bounds.Max.Y) | |
| soffs := image.Point{X: 1, Y: 0} | |
| draw.Draw(img, rect, s.img, soffs, draw.Src) | |
| sliver := image.Rect(bounds.Max.X-1, bounds.Min.Y, bounds.Max.X, bounds.Max.Y) | |
| draw.Draw(img, sliver, &image.Uniform{color.White}, soffs, draw.Src) | |
| // Fill in new edge | |
| x, y := float64(s.width-1), float64(s.height)/2 | |
| s.sg += rand.Float64()*0.1 - 0.05 | |
| if s.sg < 0 { | |
| s.sg = 0 | |
| } else if s.sg > 1 { | |
| s.sg = 1 | |
| } | |
| w := s.sg / float64(s.grains-1) | |
| for i := range s.grains { | |
| a := 0.1 - float64(i)/float64(s.grains*10) | |
| ai := uint8(a * 256) | |
| //dy := s.gage * float64(i) * w | |
| //dy := s.gage * math.Sin(float64(i)*w) | |
| dy := s.gage * math.Sin(math.Sin(float64(i)*w)) | |
| if i > 0 { | |
| point(img, x, y+dy, s.r, color.RGBA{0, 0, 0, ai}) | |
| } | |
| point(img, x, y-dy, s.r, color.RGBA{0, 0, 0, ai}) | |
| } | |
| s.img = img | |
| return img | |
| } | |
| func point(img *image.RGBA, x, y, r float64, c color.RGBA) { | |
| graphics2d.FillPath(img, graphics2d.Square([]float64{x, y}, r), graphics2d.NewPen(c, 1)) | |
| } |
Author
jphsd
commented
Aug 5, 2025
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment