11

I am new to Go and was working through a problem in The Go Programming Language. The code should create GIF animations out of random Lissajous figures with the images being produced in the different colors from palate:

// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/

// Run with "web" command-line argument for web server.
// See page 13.
//!+main

// Lissajous generates GIF animations of random Lissajous figures.
package main

import (
    "image"
    "image/color"
    "image/gif"
    "io"
    "math"
    "math/rand"
    "os"
)

//!-main
// Packages not needed by version in book.
import (
    "log"
    "net/http"
    "time"
)

//!+main
// #00ff55
var palette = []color.Color{color.RGBA{0x00, 0xff, 0x55, 0xFF}, color.Black, color.RGBA{0x00, 0x00, 0xff, 0xFF}, color.RGBA{0xff, 0x00, 0xff, 0xFF}}

const (
    whiteIndex = 0 // first color in palette
)

func main() {
    //!-main
    // The sequence of images is deterministic unless we seed
    // the pseudo-random number generator using the current time.
    // Thanks to Randall McPherson for pointing out the omission.
    rand.Seed(time.Now().UTC().UnixNano())

    if len(os.Args) > 1 && os.Args[1] == "web" {
        //!+http
        handler := func(w http.ResponseWriter, r *http.Request) {
            lissajous(w)
        }
        http.HandleFunc("/", handler)
        //!-http
        log.Fatal(http.ListenAndServe("localhost:8000", nil))
        return
    }
    //!+main
    lissajous(os.Stdout)
}

func lissajous(out io.Writer) {
    const (
        cycles  = 5     // number of complete x oscillator revolutions
        res     = 0.001 // angular resolution
        size    = 100   // image canvas covers [-size..+size]
        nframes = 64    // number of animation frames
        delay   = 8     // delay between frames in 10ms units
    )
    freq := rand.Float64() * 3.0 // relative frequency of y oscillator
    anim := gif.GIF{LoopCount: nframes}
    phase := 0.0 // phase difference
  colorIndex := 2
  for i := 0; i < nframes; i++ {
    rect := image.Rect(0, 0, 2*size+1, 2*size+1)
        img := image.NewPaletted(rect, palette)
        for t := 0.0; t < cycles*2*math.Pi; t += res {
            x := math.Sin(t)
            y := math.Sin(t*freq + phase)
      img.SetColorIndex(size+int(x*size+0.5), size+int(y*size+0.5), colorIndex)
      colorIndex++
        }
        phase += 0.1
        anim.Delay = append(anim.Delay, delay)
        anim.Image = append(anim.Image, img)
    }
    gif.EncodeAll(out, &anim) // NOTE: ignoring encoding errors
}

//!-main

Here is the error I am getting

lissajous/main.go:76: cannot use colorIndex (type int) as type uint8 in argument to img.SetColorIndex

Is there a difference between int and uint8 types or something?

nomel7
  • 1,523
  • 3
  • 12
  • 20
  • 2
    Compiler tells you that __yes__, there's a difference. – u_mulder Dec 17 '16 at 18:47
  • How do I transform one to another? Why does it work if I use a magic constant but it doesn't work when using a variable as the parameter? – nomel7 Dec 17 '16 at 18:48
  • I cannot find anything for the difference between the types int and uint8. Is uint8 not the standard int type? – nomel7 Dec 17 '16 at 18:51
  • 1
    `u` in `uint` means `unsigned`. So you cant assign `-23` to `uint` type. But can assign to `int`. – u_mulder Dec 17 '16 at 18:53
  • 1
    As for type conversions - https://tour.golang.org/basics/13 – u_mulder Dec 17 '16 at 18:54
  • Thanks. The value in number in question though `colorIndex` is defined to `2`. So how is is not a valid parameter? Is there some weird type introspection going on here? I am very new to Go :) – nomel7 Dec 17 '16 at 19:02
  • It's not a valid parameter becuase it has type that is not supported. – u_mulder Dec 17 '16 at 19:06

2 Answers2

8

The type of colorIndex is int. The argument type is uint8. An int cannot be assigned to a uint8. Here are some options for fixing the program:

  • Declare colorIndex as an untyped constant.

    const colorIndex = 2
    
  • Declare colorIndex as uint8 type:

    colorIndex := uint8(3)
    
  • Convert the value at the call:

    img.SetColorIndex(size+int(x*size+0.5), size+int(y*size+0.5), uint8(colorIndex))
    

You can replace all uses of uint8 in this answer with byte because byte is an alias for uint8.

Charlie Tumahai
  • 113,709
  • 12
  • 249
  • 242
1

In variable declarations, a default type is used, and in your case, colorIndex := 2, i.e. colorIndex becomes int, not uint8.

From the docs ( https://golang.org/ref/spec#Short_variable_declarations ): "If a type is present, each variable is given that type. Otherwise, each variable is given the type of the corresponding initialization value in the assignment. If that value is an untyped constant, it is first converted to its default type;..."

"var i = 42 // i is int"

and then

"An untyped constant has a default type which is the type to which the constant is implicitly converted in contexts where a typed value is required, for instance, in a short variable declaration such as i := 0 where there is no explicit type. The default type of an untyped constant is bool, rune, int, float64, complex128 or string respectively, depending on whether it is a boolean, rune, integer, floating-point, complex, or string constant."

So to get uint8, you should either explicitly declare colorIndex as uint8 var colorIndex uint8 = 2 or cast uint8 in img.SetColorIndex as : img.SetColorIndex(size+int(x*size+0.5), size+int(y*size+0.5), uint8(colorIndex))

oharlem
  • 1,030
  • 7
  • 12