emulator: improvements and integration
This commit is contained in:
@@ -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
|
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
|
License
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
|||||||
@@ -3,10 +3,12 @@ package emulator
|
|||||||
import (
|
import (
|
||||||
"image"
|
"image"
|
||||||
"image/color"
|
"image/color"
|
||||||
"log"
|
"os"
|
||||||
|
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"golang.org/x/exp/shiny/driver"
|
"golang.org/x/exp/shiny/driver"
|
||||||
"golang.org/x/exp/shiny/imageutil"
|
"golang.org/x/exp/shiny/imageutil"
|
||||||
"golang.org/x/exp/shiny/screen"
|
"golang.org/x/exp/shiny/screen"
|
||||||
@@ -14,10 +16,8 @@ import (
|
|||||||
"golang.org/x/mobile/event/size"
|
"golang.org/x/mobile/event/size"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
const DefaultPixelPitch = 12
|
||||||
black = color.RGBA{0x00, 0x00, 0x00, 0xff}
|
const windowTitle = "RGB led matrix emulator"
|
||||||
red = color.RGBA{0x7f, 0x00, 0x00, 0x7f}
|
|
||||||
)
|
|
||||||
|
|
||||||
var margin = 10
|
var margin = 10
|
||||||
|
|
||||||
@@ -27,60 +27,81 @@ type Emulator struct {
|
|||||||
Width int
|
Width int
|
||||||
Height int
|
Height int
|
||||||
|
|
||||||
leds []color.Color
|
leds []color.Color
|
||||||
w screen.Window
|
w screen.Window
|
||||||
isReady bool
|
s screen.Screen
|
||||||
|
wg sync.WaitGroup
|
||||||
|
|
||||||
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,
|
||||||
|
}
|
||||||
|
|
||||||
|
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() {
|
func (e *Emulator) Init() {
|
||||||
e.leds = make([]color.Color, 2048)
|
e.leds = make([]color.Color, 2048)
|
||||||
|
|
||||||
e.wg.Add(1)
|
e.wg.Add(1)
|
||||||
go e.init()
|
go driver.Main(e.mainWindowLoop)
|
||||||
e.wg.Wait()
|
e.wg.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Emulator) init() {
|
func (e *Emulator) mainWindowLoop(s screen.Screen) {
|
||||||
driver.Main(func(s screen.Screen) {
|
var err error
|
||||||
var err error
|
e.s = s
|
||||||
e.w, err = s.NewWindow(&screen.NewWindowOptions{
|
e.w, err = s.NewWindow(&screen.NewWindowOptions{
|
||||||
Title: "Basic Shiny Example",
|
Title: windowTitle,
|
||||||
})
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
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:
|
||||||
|
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) {
|
func (e *Emulator) Geometry() (width, height int) {
|
||||||
@@ -98,8 +119,10 @@ func (e *Emulator) Apply(leds []color.Color) error {
|
|||||||
x += margin * 2
|
x += margin * 2
|
||||||
y += margin * 2
|
y += margin * 2
|
||||||
|
|
||||||
c := e.At(col + (row * e.Width))
|
color := e.At(col + (row * e.Width))
|
||||||
e.w.Fill(image.Rect(x, y, x+e.PixelPitch, y+e.PixelPitch), c, screen.Over)
|
led := image.Rect(x, y, x+e.PixelPitch, y+e.PixelPitch)
|
||||||
|
|
||||||
|
e.w.Fill(led, color, screen.Over)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
24
matrix.go
24
matrix.go
@@ -29,7 +29,10 @@ import "C"
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"image/color"
|
"image/color"
|
||||||
|
"os"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/mcuadros/go-rpi-rgb-led-matrix/emulator"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DefaultConfig default WS281x configuration
|
// DefaultConfig default WS281x configuration
|
||||||
@@ -116,8 +119,14 @@ type RGBLedMatrix struct {
|
|||||||
leds []C.uint32_t
|
leds []C.uint32_t
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const MatrixEmulatorENV = "MATRIX_EMULATOR"
|
||||||
|
|
||||||
// NewRGBLedMatrix returns a new matrix using the given size and config
|
// 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()
|
w, h := config.geometry()
|
||||||
|
|
||||||
c := &RGBLedMatrix{
|
c := &RGBLedMatrix{
|
||||||
@@ -134,6 +143,19 @@ func NewRGBLedMatrix(config *HardwareConfig) (*RGBLedMatrix, error) {
|
|||||||
return c, nil
|
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
|
// Initialize initialize library, must be called once before other functions are
|
||||||
// called.
|
// called.
|
||||||
func (c *RGBLedMatrix) Initialize() error {
|
func (c *RGBLedMatrix) Initialize() error {
|
||||||
|
|||||||
Reference in New Issue
Block a user