sdl2-life

Conway's Game of Life with go-sdl2
git clone https://www.brianlane.com/git/sdl2-life
Log | Files | Refs | README

commit 73cba4bb2ea073e81c06b554a923badc1be8ba17
parent 3e9725f4adf29e53a5c7e9bf4c1919dc06bfaa42
Author: Brian C. Lane <bcl@brianlane.com>
Date:   Sun,  4 Dec 2022 15:05:28 -0800

Add Polylinear Gradient and some cmdline switches

oh, and fix a bug in the gradient calculation. Cast to float64 before
subtracting the uint8's not after. You get better negative numbers that
way.

Added selection of gradient type, and total age for gradient cmdline args.

Diffstat:
Mmain.go | 75++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
1 file changed, 66 insertions(+), 9 deletions(-)

diff --git a/main.go b/main.go @@ -21,8 +21,13 @@ import ( ) const ( - threshold = 0.15 - maxColorAge = 255 + threshold = 0.15 + // LinearGradient cmdline selection + LinearGradient = 0 + // PolylinearGradient cmdline selection + PolylinearGradient = 1 + // BezierGradient cmdline selection + BezierGradient = 2 ) // RLE header with variable spacing and optional rules @@ -46,6 +51,8 @@ type cmdlineArgs struct { Empty bool // Start with empty world Color bool // Color the cells based on age Colors string // Comma separated color hex triplets + Gradient int // Gradient algorithm to use + MaxAge int // Maximum age for gradient colors Port int // Port to listen to Host string // Host IP to bind to Server bool // Launch an API server when true @@ -68,6 +75,8 @@ var cfg = cmdlineArgs{ Empty: false, Color: false, Colors: "#4682b4,#ffffff", + Gradient: 0, + MaxAge: 255, Port: 3051, Host: "127.0.0.1", Server: false, @@ -90,6 +99,8 @@ func parseArgs() { flag.BoolVar(&cfg.Empty, "empty", cfg.Empty, "Start with empty world") flag.BoolVar(&cfg.Color, "color", cfg.Color, "Color cells based on age") flag.StringVar(&cfg.Colors, "colors", cfg.Colors, "Comma separated color hex triplets") + flag.IntVar(&cfg.Gradient, "gradient", cfg.Gradient, "Gradient type. 0=Linear 1=Polylinear 2=Bezier") + flag.IntVar(&cfg.MaxAge, "age", cfg.MaxAge, "Maximum age for gradient colors") flag.IntVar(&cfg.Port, "port", cfg.Port, "Port to listen to") flag.StringVar(&cfg.Host, "host", cfg.Host, "Host IP to bind to") flag.BoolVar(&cfg.Server, "server", cfg.Server, "Launch an API server") @@ -113,6 +124,21 @@ type Gradient struct { points []RGBAColor } +// Print prints the gradient values to the console +func (g *Gradient) Print() { + fmt.Printf("controls:\n%#v\n\n", g.controls) + for i := range g.points { + fmt.Printf("%d = %#v\n", i, g.points[i]) + } +} + +// Append adds the points from a gradient to this one at an insertion point +func (g *Gradient) Append(from Gradient, start int) { + for i, p := range from.points { + g.points[start+i] = p + } +} + // NewLinearGradient returns a Linear Gradient with pre-computed colors for every age // from https://bsouthga.dev/posts/color-gradients-with-python // @@ -124,15 +150,40 @@ func NewLinearGradient(colors []RGBAColor, maxAge int) Gradient { start := gradient.controls[0] end := gradient.controls[1] - for t := 1; t <= maxAge; t++ { - r := uint8(start.r + uint8((float64(t)/float64(maxAge))*float64(end.r-start.r))) - g := uint8(start.g + uint8((float64(t)/float64(maxAge))*float64(end.g-start.g))) - b := uint8(start.b + uint8((float64(t)/float64(maxAge))*float64(end.b-start.b))) - gradient.points[t-1] = RGBAColor{r, g, b, 255} + for t := 0; t < maxAge; t++ { + r := uint8(float64(start.r) + (float64(t)/float64(maxAge-1))*(float64(end.r)-float64(start.r))) + g := uint8(float64(start.g) + (float64(t)/float64(maxAge-1))*(float64(end.g)-float64(start.g))) + b := uint8(float64(start.b) + (float64(t)/float64(maxAge-1))*(float64(end.b)-float64(start.b))) + gradient.points[t] = RGBAColor{r, g, b, 255} } return gradient } +// NewPolylinearGradient returns a gradient that is linear between all control colors +func NewPolylinearGradient(colors []RGBAColor, maxAge int) Gradient { + // TODO check number of colors and return error if < 2 + gradient := Gradient{controls: colors, points: make([]RGBAColor, maxAge)} + + n := int(float64(maxAge) / float64(len(colors)-1)) + gradient.Append(NewLinearGradient(colors, n), 0) + + if len(colors) == 2 { + return gradient + } + + for i := 1; i < len(colors)-1; i++ { + if i == len(colors)-2 { + // The last group may need to be extended if it doesn't fill all the way to the end + remainder := maxAge - ((i + 1) * n) + gradient.Append(NewLinearGradient(colors[i:i+1], n+remainder), (i * n)) + } else { + gradient.Append(NewLinearGradient(colors[i:i+1], n), (i * n)) + } + } + + return gradient +} + // FactorialCache saves the results for factorial calculations for faster access type FactorialCache struct { cache map[int]float64 @@ -878,8 +929,14 @@ func InitializeGame() *LifeGame { } // Build the color gradient - // game.gradient = NewLinearGradient(colors, maxColorAge) - game.gradient = NewBezierGradient(colors, maxColorAge) + switch cfg.Gradient { + case LinearGradient: + game.gradient = NewLinearGradient(colors, cfg.MaxAge) + case PolylinearGradient: + game.gradient = NewPolylinearGradient(colors, cfg.MaxAge) + case BezierGradient: + game.gradient = NewBezierGradient(colors, cfg.MaxAge) + } return game }