Last active
December 14, 2020 13:47
-
-
Save suapapa/d570273588f340abe966d89582c9daab to your computer and use it in GitHub Desktop.
xmas-tree
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 ( | |
"image" | |
"image/color" | |
"log" | |
"math" | |
"math/rand" | |
"time" | |
"periph.io/x/periph/conn/display" | |
"periph.io/x/periph/conn/spi/spireg" | |
"periph.io/x/periph/devices/apa102" | |
"periph.io/x/periph/host" | |
) | |
const ( | |
starCnt = 30 | |
) | |
type Star struct { | |
c color.Color | |
lifeTime time.Time | |
} | |
type Stars struct { | |
cnt int | |
stars []Star | |
img *image.NRGBA | |
} | |
func NewStars(cnt int) *Stars { | |
return &Stars{ | |
cnt: cnt, | |
stars: make([]Star, cnt), | |
img: image.NewNRGBA(image.Rect(0, 0, cnt, 1)), | |
} | |
} | |
func (s *Stars) Refresh(t time.Time) image.Image { | |
for i, b := range s.stars { | |
if b.lifeTime.Before(t) { | |
s.stars[i].c = &HSV{ | |
H: rand.Float64(), | |
S: rand.Float64(), | |
V: 1.0, | |
} | |
s.stars[i].lifeTime = t.Add( | |
time.Second + time.Duration(rand.Intn(2000))*time.Millisecond, | |
) | |
} | |
} | |
for x := 0; x < s.cnt; x++ { | |
s.img.SetNRGBA(x, 0, NRGBA(s.stars[x].c)) | |
} | |
return s.img | |
} | |
// HSV represents HSV color space | |
type HSV struct { | |
H, S, V float64 | |
} | |
// RGBA implements color.Color interface | |
func (c *HSV) RGBA() (r, g, b, a uint32) { | |
fr, fg, fb := c.rgb() | |
r = uint32(math.Round(fr * 0xffffffff)) | |
g = uint32(math.Round(fg * 0xffffffff)) | |
b = uint32(math.Round(fb * 0xffffffff)) | |
a = 0xffffffff | |
return | |
} | |
func (c *HSV) rgb() (r, g, b float64) { | |
if c.S == 0 { //HSV from 0 to 1 | |
r = c.V * 255 | |
g = c.V * 255 | |
b = c.V * 255 | |
} else { | |
h := c.H * 6 | |
if h == 6 { | |
h = 0 | |
} //H must be < 1 | |
i := math.Floor(h) //Or ... var_i = floor( var_h ) | |
v1 := c.V * (1 - c.S) | |
v2 := c.V * (1 - c.S*(h-i)) | |
v3 := c.V * (1 - c.S*(1-(h-i))) | |
if i == 0 { | |
r = c.V | |
g = v3 | |
b = v1 | |
} else if i == 1 { | |
r = v2 | |
g = c.V | |
b = v1 | |
} else if i == 2 { | |
r = v1 | |
g = c.V | |
b = v3 | |
} else if i == 3 { | |
r = v1 | |
g = v2 | |
b = c.V | |
} else if i == 4 { | |
r = v3 | |
g = v1 | |
b = c.V | |
} else { | |
r = c.V | |
g = v1 | |
b = v2 | |
} | |
} | |
return r, g, b | |
} | |
func init() { | |
rand.Seed(time.Now().UnixNano()) | |
} | |
func main() { | |
if _, err := host.Init(); err != nil { | |
log.Fatal(err) | |
} | |
d := getLEDs() | |
stars := NewStars(starCnt) | |
tk := time.NewTicker(time.Second / 10) // FPS | |
for t := range tk.C { | |
img := stars.Refresh(t) | |
if err := d.Draw(d.Bounds(), img, image.Point{}); err != nil { | |
log.Fatal(err) | |
} | |
} | |
} | |
// getLEDs returns an *apa102.Dev, or fails back to *screen.Dev if no SPI port | |
// is found. | |
func getLEDs() display.Drawer { | |
s, err := spireg.Open("") | |
if err != nil { | |
panic(err) | |
} | |
// Change the option values to see their effects. | |
var dispOpts = &apa102.Opts{ | |
NumPixels: starCnt, // 150 LEDs is a common strip length. | |
Intensity: 128, // Full blinding power. | |
Temperature: 5000, // More pleasing white balance than NeutralTemp. | |
DisableGlobalPWM: false, // Use full 13 bits range. | |
} | |
d, err := apa102.New(s, dispOpts) | |
if err != nil { | |
log.Fatal(err) | |
} | |
return d | |
} | |
// NRGBA convert color.Color to color.NRGBA | |
func NRGBA(c color.Color) color.NRGBA { | |
r, g, b, _ := c.RGBA() | |
fr, fg, fb := float64(r), float64(g), float64(b) | |
return color.NRGBA{ | |
R: uint8(math.Round(fr * 0xff)), | |
G: uint8(math.Round(fg * 0xff)), | |
B: uint8(math.Round(fb * 0xff)), | |
A: 0xff, | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment