From 741ec8a7436857a88aa6d075da7d7bd48966edcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1ximo=20Cuadros?= Date: Fri, 10 Mar 2017 12:40:45 +0100 Subject: [PATCH] emulator: improvements and integration --- README.md | 9 ++++ emulator/emulator.go | 115 ++++++++++++++++++++++++++----------------- matrix.go | 24 ++++++++- 3 files changed, 101 insertions(+), 47 deletions(-) diff --git a/README.md b/README.md index f9c5c09..6ef4f25 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,15 @@ The image of the header was recorded using this few lines, the running _Mario_ g Check the folder [`examples`](https://github.com/mcuadros/go-rpi-rgb-led-matrix/tree/master/examples) folder for more examples + +Matrix Emulation +---------------- + +As part of the library an small Matrix emulator is provided. The emulator renderize a virtual RGB matrix on a window in your desktop, without needing a real RGB matrix connected to your computer. + +To execute the emulator set the `MATRIX_EMULATOR` environment variable to `1`, then when `NewRGBLedMatrix` is used, a `emulator.Emulator` is returned instead of a interface the real board. + + License ------- diff --git a/emulator/emulator.go b/emulator/emulator.go index 4c837ac..8dd6410 100644 --- a/emulator/emulator.go +++ b/emulator/emulator.go @@ -3,10 +3,12 @@ package emulator import ( "image" "image/color" - "log" + "os" "sync" + "fmt" + "golang.org/x/exp/shiny/driver" "golang.org/x/exp/shiny/imageutil" "golang.org/x/exp/shiny/screen" @@ -14,10 +16,8 @@ import ( "golang.org/x/mobile/event/size" ) -var ( - black = color.RGBA{0x00, 0x00, 0x00, 0xff} - red = color.RGBA{0x7f, 0x00, 0x00, 0x7f} -) +const DefaultPixelPitch = 12 +const windowTitle = "RGB led matrix emulator" var margin = 10 @@ -27,60 +27,81 @@ type Emulator struct { Width int Height int - leds []color.Color - w screen.Window + leds []color.Color + w screen.Window + s screen.Screen + wg sync.WaitGroup + isReady bool +} + +func NewEmulator(w, h, pixelPitch int, autoInit bool) *Emulator { + e := &Emulator{ + PixelPitch: pixelPitch, + Gutter: int(float64(pixelPitch) * 0.66), + Width: w, + Height: h, + } - wg sync.WaitGroup + if autoInit { + e.Init() + } + + return e } +// Init initialize the emulator, creating a new Window and waiting until is +// painted. If something goes wrong the function panics func (e *Emulator) Init() { e.leds = make([]color.Color, 2048) e.wg.Add(1) - go e.init() + go driver.Main(e.mainWindowLoop) e.wg.Wait() } -func (e *Emulator) init() { - driver.Main(func(s screen.Screen) { - var err error - e.w, err = s.NewWindow(&screen.NewWindowOptions{ - Title: "Basic Shiny Example", - }) +func (e *Emulator) mainWindowLoop(s screen.Screen) { + var err error + e.s = s + e.w, err = s.NewWindow(&screen.NewWindowOptions{ + Title: windowTitle, + }) - if err != nil { - panic(err) - } + if err != nil { + panic(err) + } - defer e.w.Release() - - var sz size.Event - for { - evn := e.w.NextEvent() - switch evn := evn.(type) { - case paint.Event: - for _, r := range imageutil.Border(sz.Bounds(), margin) { - e.w.Fill(r, red, screen.Src) - } - - e.w.Fill(sz.Bounds().Inset(margin), black, screen.Src) - e.w.Publish() - if e.isReady { - continue - } - - e.Apply(make([]color.Color, 2048)) - e.wg.Done() - e.isReady = true - case size.Event: - sz = evn - - case error: - log.Print(e) + defer e.w.Release() + + var sz size.Event + for { + evn := e.w.NextEvent() + switch evn := evn.(type) { + case paint.Event: + e.drawContext(sz) + if e.isReady { + continue } + + e.Apply(make([]color.Color, 2048)) + e.wg.Done() + e.isReady = true + case size.Event: + sz = evn + + case error: + fmt.Fprintln(os.Stderr, e) } - }) + } +} + +func (e *Emulator) drawContext(sz size.Event) { + for _, r := range imageutil.Border(sz.Bounds(), margin) { + e.w.Fill(r, color.White, screen.Src) + } + + e.w.Fill(sz.Bounds().Inset(margin), color.Black, screen.Src) + e.w.Publish() } func (e *Emulator) Geometry() (width, height int) { @@ -98,8 +119,10 @@ func (e *Emulator) Apply(leds []color.Color) error { x += margin * 2 y += margin * 2 - c := e.At(col + (row * e.Width)) - e.w.Fill(image.Rect(x, y, x+e.PixelPitch, y+e.PixelPitch), c, screen.Over) + color := e.At(col + (row * e.Width)) + led := image.Rect(x, y, x+e.PixelPitch, y+e.PixelPitch) + + e.w.Fill(led, color, screen.Over) } } diff --git a/matrix.go b/matrix.go index 034d689..165bd5f 100644 --- a/matrix.go +++ b/matrix.go @@ -29,7 +29,10 @@ import "C" import ( "fmt" "image/color" + "os" "unsafe" + + "github.com/mcuadros/go-rpi-rgb-led-matrix/emulator" ) // DefaultConfig default WS281x configuration @@ -116,8 +119,14 @@ type RGBLedMatrix struct { leds []C.uint32_t } +const MatrixEmulatorENV = "MATRIX_EMULATOR" + // NewRGBLedMatrix returns a new matrix using the given size and config -func NewRGBLedMatrix(config *HardwareConfig) (*RGBLedMatrix, error) { +func NewRGBLedMatrix(config *HardwareConfig) (Matrix, error) { + if isMatrixEmulator() { + return buildMatrixEmulator(config), nil + } + w, h := config.geometry() c := &RGBLedMatrix{ @@ -134,6 +143,19 @@ func NewRGBLedMatrix(config *HardwareConfig) (*RGBLedMatrix, error) { return c, nil } +func isMatrixEmulator() bool { + if os.Getenv(MatrixEmulatorENV) == "1" { + return true + } + + return false +} + +func buildMatrixEmulator(config *HardwareConfig) Matrix { + w, h := config.geometry() + return emulator.NewEmulator(w, h, emulator.DefaultPixelPitch, true) +} + // Initialize initialize library, must be called once before other functions are // called. func (c *RGBLedMatrix) Initialize() error {