Skip to content

Commit

Permalink
Merge pull request #20 from richardlt/features/animateSoftware
Browse files Browse the repository at this point in the history
feat(animate): implement player for glediator animations
  • Loading branch information
richardlt committed Feb 6, 2019
2 parents df9ae4a + b044144 commit 0eeee53
Show file tree
Hide file tree
Showing 14 changed files with 255 additions and 3 deletions.
3 changes: 2 additions & 1 deletion .debpacker.yml
Expand Up @@ -5,6 +5,7 @@ copy-files:
- "./matrix-package/themes/*:themes"
- "./matrix-package/fonts/*:fonts"
- "./matrix-package/images/*:images"
- "./matrix-package/animations/*:animations"
- "./matrix-package/gamepad/public/*:gamepad/public"
- "./matrix-package/emulator/public/*:emulator/public"
version: "0.0.1"
Expand All @@ -13,7 +14,7 @@ maintainer: "richard.le.terrier@gmail.com"
systemd-configuration:
user: "root"
after: network.target
args: ["start", "--log-level", "info", "--gamepad-port", "80", "core", "device", "gamepad", "emulator", "demo", "zigzag", "yumyum", "clock", "draw", "blocks", "getout"]
args: ["start", "--log-level", "info", "--gamepad-port", "80", "core", "device", "gamepad", "emulator", "demo", "zigzag", "yumyum", "clock", "draw", "blocks", "getout", "animate"]
stop-command: /bin/kill $MAINPID
restart: always
wanted-by: multi-user.target
Expand Down
1 change: 1 addition & 0 deletions Makefile
Expand Up @@ -42,6 +42,7 @@ package:
cp -R themes matrix-package/
cp -R fonts matrix-package/
cp -R images matrix-package/
cp -R animations matrix-package/
cp -R gamepad/build/default/. matrix-package/gamepad/public/
cp -R emulator/client/public/. matrix-package/emulator/public/
zip -r matrix.zip matrix-package
Expand Down
1 change: 1 addition & 0 deletions README.md
Expand Up @@ -24,6 +24,7 @@ Video game console operating system that displays on a 16*9 RGB LED matrix.
| <img src="https://raw.githubusercontent.com/richardlt/matrix/master/docs/blocks.png" width="60"/> | Blocks | A puzzle game, score a maximum of points by clearing complete lines. | <img src="https://raw.githubusercontent.com/richardlt/matrix/master/docs/blocks.gif" width="150"/> |
| <img src="https://raw.githubusercontent.com/richardlt/matrix/master/docs/getout.png" width="60"/> | Getout | A labyrinth game, try to get out if you can. | <img src="https://raw.githubusercontent.com/richardlt/matrix/master/docs/getout.gif" width="150"/> |
| <img src="https://raw.githubusercontent.com/richardlt/matrix/master/docs/rollup-dice.png" width="60"/> | Rollup dice | Random dice generator (https://github.com/gwenker/matrix-rollup-dice). | <img src="https://raw.githubusercontent.com/richardlt/matrix/master/docs/rollup-dice.gif" width="150"/> |
| <img src="https://raw.githubusercontent.com/richardlt/matrix/master/docs/animate.png" width="60"/> | Animate | Player for animations generated with Glediator (http://www.solderlab.de/index.php/software/glediator). | <img src="https://raw.githubusercontent.com/richardlt/matrix/master/docs/animate.gif" width="150"/> |

## Matrix types

Expand Down
203 changes: 203 additions & 0 deletions animate/daemon.go
@@ -0,0 +1,203 @@
package animate

import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"time"

"github.com/ovh/cds/sdk"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"

"github.com/richardlt/matrix/sdk-go/common"
"github.com/richardlt/matrix/sdk-go/software"
)

// Start the animate software.
func Start(uri string) error {
logrus.Infof("Start animate for uri %s\n", uri)

a := &animate{}

// list animation headers and set index
if err := filepath.Walk("./animations", func(path string, info os.FileInfo, err error) error {
if !info.IsDir() && strings.HasSuffix(path, ".json") {
buf, err := ioutil.ReadFile(path)
if err != nil {
return sdk.WithStack(err)
}

var h header
if err := json.Unmarshal(buf, &h); err != nil {
return errors.WithStack(err)
}

a.headers = append(a.headers, h)
}
return nil
}); err != nil {
return errors.WithStack(err)
}

return software.Connect(uri, a, true)
}

type header struct {
Name string `json:"name"`
Width int `json:"width"`
Height int `json:"height"`
FPS int `json:"fps"`
}

type animation []byte

func (a animation) readFrame(width, height, index int) software.Image {
pixels := 3 * width * height
start := index * pixels
end := start + pixels
buf := a[start:end]

var colors []*common.Color
mapColors := map[string]uint64{}
mask := make([]uint64, width*height)
var cursor int
for i := range mask {
c := common.Color{
R: uint64(buf[cursor]),
G: uint64(buf[cursor+1]),
B: uint64(buf[cursor+2]),
A: 1,
}
key := fmt.Sprintf("%d%d%d", c.R, c.G, c.B)
if v, ok := mapColors[key]; !ok {
colors = append(colors, &c)
mask[i] = uint64(len(colors) - 1)
mapColors[key] = mask[i]
} else {
mask[i] = v
}
cursor += 3
}

return software.Image{
Width: uint64(width),
Height: uint64(height),
Colors: colors,
Mask: mask,
}
}

type animate struct {
api software.API
layer software.Layer
imageDriver *software.ImageDriver
cancel context.CancelFunc
headers []header
index int
}

func (a *animate) Init(api software.API) (err error) {
logrus.Debug("Init animate")

a.api = api

i := api.GetImageFromLocal("animate")

api.SetConfig(software.ConnectRequest_SoftwareData_Config{
Logo: &i,
MinPlayerCount: 1,
MaxPlayerCount: 1,
})

a.layer, err = api.NewLayer()
if err != nil {
return err
}

a.imageDriver, err = a.layer.NewImageDriver()
if err != nil {
return err
}
a.imageDriver.OnEnd(func() { a.api.Print() })

return api.Ready()
}

func (a *animate) Start(playerCount uint64) { a.play() }

func (a *animate) Close() { a.reset() }

func (a *animate) ActionReceived(slot uint64, cmd common.Command) {
switch cmd {
case common.Command_LEFT_UP:
if a.index < 1 {
a.index = len(a.headers) - 1
} else {
a.index--
}
a.play()
case common.Command_RIGHT_UP:
if a.index+1 == len(a.headers) {
a.index = 0
} else {
a.index++
}
a.play()
}
}

func (a *animate) reset() {
if a.cancel != nil {
a.cancel()
}
}

func (a *animate) play() {
a.reset()

a.layer.Clean()
a.api.Print()

if len(a.headers) == 0 {
return
}

var anim animation
var err error
anim, err = ioutil.ReadFile(fmt.Sprintf("./animations/%s", a.headers[a.index].Name))
if err != nil {
logrus.Error(errors.WithStack(err))
return
}

ctx, cancel := context.WithCancel(context.Background())
a.cancel = cancel

h := a.headers[a.index]

ticker := time.NewTicker(time.Second / time.Duration(h.FPS))
defer ticker.Stop()
maxIndex := len(anim) / (h.Width * h.Height * 3)
index := 0
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
a.imageDriver.Render(
anim.readFrame(h.Width, h.Height, index),
common.Coord{X: 8, Y: 4}, // middle of the screen
)
if index+1 == maxIndex {
index = 0
} else {
index++
}
}
}
}
Binary file added animations/sample-one.dat
Binary file not shown.
6 changes: 6 additions & 0 deletions animations/sample-one.json
@@ -0,0 +1,6 @@
{
"name": "sample-one.dat",
"width": 16,
"height": 9,
"fps": 25
}
Binary file added animations/sample-three.dat
Binary file not shown.
6 changes: 6 additions & 0 deletions animations/sample-three.json
@@ -0,0 +1,6 @@
{
"name": "sample-three.dat",
"width": 16,
"height": 9,
"fps": 25
}
Binary file added animations/sample-two.dat
Binary file not shown.
6 changes: 6 additions & 0 deletions animations/sample-two.json
@@ -0,0 +1,6 @@
{
"name": "sample-two.dat",
"width": 16,
"height": 9,
"fps": 25
}
Binary file added docs/animate.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/animate.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 24 additions & 0 deletions images/animate.json
@@ -0,0 +1,24 @@
{
"name": "animate",
"height": 7,
"width": 8,
"colors": [
{ "r": 205, "g": 0, "b": 0, "a": 1 },
{ "r": 255, "g": 0, "b": 0, "a": 1 },
{ "r": 0, "g": 205, "b": 0, "a": 1 },
{ "r": 0, "g": 255, "b": 0, "a": 1 },
{ "r": 0, "g": 0, "b": 205, "a": 1 },
{ "r": 0, "g": 0, "b": 255, "a": 1 },
{ "r": 255, "g": 255, "b": 0, "a": 1 },
{ "r": 205, "g": 205, "b": 0, "a": 1 }
],
"mask": [
3, 2, 3, 7, 6, 7, 5, 4,
1, 3, 2, 3, 7, 6, 7, 5,
0, 1, 3, 2, 3, 7, 6, 7,
1, 0, 1, 3, 2, 3, 7, 6,
5, 1, 0, 1, 3, 2, 3, 7,
4, 5, 1, 0, 1, 3, 2, 3,
5, 4, 5, 1, 0, 1, 3, 2
]
}
8 changes: 6 additions & 2 deletions main.go
Expand Up @@ -5,6 +5,10 @@ import (
"os"

"github.com/pkg/errors"
"github.com/sirupsen/logrus"
cli "gopkg.in/urfave/cli.v1"

"github.com/richardlt/matrix/animate"
"github.com/richardlt/matrix/blocks"
"github.com/richardlt/matrix/clock"
"github.com/richardlt/matrix/core"
Expand All @@ -16,8 +20,6 @@ import (
"github.com/richardlt/matrix/getout"
"github.com/richardlt/matrix/yumyum"
"github.com/richardlt/matrix/zigzag"
"github.com/sirupsen/logrus"
cli "gopkg.in/urfave/cli.v1"
)

func main() {
Expand Down Expand Up @@ -98,6 +100,8 @@ func startAction(c *cli.Context) error {
cs = append(cs, component(func() error { return blocks.Start(c.String("core-uri")) }))
case "getout":
cs = append(cs, component(func() error { return getout.Start(c.String("core-uri")) }))
case "animate":
cs = append(cs, component(func() error { return animate.Start(c.String("core-uri")) }))
default:
return errors.New("Invalid given component name")
}
Expand Down

0 comments on commit 0eeee53

Please sign in to comment.