Compare commits

...

23 Commits

Author SHA1 Message Date
Martyn b9d8f7de92 Newer Drone requires kind: kubernetes
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
Signed-off-by: Martyn Ranyard <m@rtyn.berlin>
2021-01-03 11:55:29 +01:00
Martyn 5655ddd56e Trigger CI
continuous-integration/drone/push Build was killed Details
Signed-off-by: Martyn Ranyard <m@rtyn.berlin>
2021-01-03 11:53:31 +01:00
Martyn b898c0c669 Merge branch 'master' of ssh://git-ssh.martyn.berlin:2222/martyn/LEDController
continuous-integration/drone/push Build was killed Details
Signed-off-by: Martyn Ranyard <m@rtyn.berlin>
2021-01-03 11:48:48 +01:00
Martyn 568654d88e Trigger CI
Signed-off-by: Martyn Ranyard <m@rtyn.berlin>
2021-01-03 11:48:41 +01:00
Martyn 2709d98125 moarpatterns (#1)
Make the comparison work

Allow color setting

types.... yeah... I know about them.

single-colour plasma, I hope

Reviewed-on: #1
Co-Authored-By: Martyn <m@rtyn.berlin>
Co-Committed-By: Martyn <m@rtyn.berlin>
2021-01-03 10:46:39 +00:00
Martyn 10cef66e13 zero-length override
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
Signed-off-by: Martyn Ranyard <m@rtyn.berlin>
2020-07-13 18:35:53 +02:00
Martyn ffec6c8260 Rest of the uint64 stuff
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
Signed-off-by: Martyn Ranyard <m@rtyn.berlin>
2020-07-13 18:16:50 +02:00
Martyn 4d72b04cc1 Uint64 because time...
continuous-integration/drone/push Build is failing Details
Signed-off-by: Martyn Ranyard <m@rtyn.berlin>
2020-07-13 18:13:44 +02:00
Martyn 7a5bb1108a Ensure we have a multiple of the delay in duration, and tick by the delay
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
Signed-off-by: Martyn Ranyard <m@rtyn.berlin>
2020-07-13 17:46:11 +02:00
Martyn 8c3df1dfa2 Vary framerate by env
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
Signed-off-by: Martyn Ranyard <m@rtyn.berlin>
2020-07-13 17:14:02 +02:00
Martyn 8edab5ba5e New defaults and return to queue
continuous-integration/drone/tag Build is passing Details
Signed-off-by: Martyn Ranyard <m@rtyn.berlin>
2020-07-13 16:37:14 +02:00
Martyn c346052895 Overrides!
continuous-integration/drone/tag Build was killed Details
Signed-off-by: Martyn Ranyard <m@rtyn.berlin>
2020-06-28 18:36:33 +02:00
Martyn d534eb36cd Fix centering of plasma
Signed-off-by: Martyn Ranyard <m@rtyn.berlin>
2020-06-28 18:33:42 +02:00
Martyn 688084094a Foolish failure
continuous-integration/drone/tag Build was killed Details
Signed-off-by: Martyn Ranyard <m@rtyn.berlin>
2020-05-19 13:05:13 +02:00
Martyn fda7e29175 typo in Dockerfile
continuous-integration/drone/tag Build was killed Details
Signed-off-by: Martyn Ranyard <m@rtyn.berlin>
2020-05-19 13:01:46 +02:00
Martyn a9e465f8b3 Fixup Dockerfile to new project structure
Signed-off-by: Martyn Ranyard <m@rtyn.berlin>
2020-05-19 12:56:40 +02:00
Martyn 990e30eb2f Use the makefile for deps also for non-publish builds
Signed-off-by: Martyn Ranyard <m@rtyn.berlin>
2020-05-19 12:55:26 +02:00
Martyn c853a07743 Use the makefile for deps
Signed-off-by: Martyn Ranyard <m@rtyn.berlin>
2020-05-19 12:53:33 +02:00
Martyn 5f8e68662f Working plasma pattern
Signed-off-by: Martyn Ranyard <m@rtyn.berlin>
2020-05-19 12:48:47 +02:00
Martyn 34bb137e7b Go fmt
Signed-off-by: Martyn Ranyard <m@rtyn.berlin>
2020-05-19 12:48:24 +02:00
Martyn c41fca9c5a Fix the universes bug
Signed-off-by: Martyn Ranyard <m@rtyn.berlin>
2020-05-19 12:47:56 +02:00
Martyn 209bebe51d refactor dynamic universes
Signed-off-by: Martyn Ranyard <m@rtyn.berlin>
2020-05-19 10:45:42 +02:00
Martyn 3e04b69b97 More refactor plus start for the emulator
Signed-off-by: Martyn Ranyard <m@rtyn.berlin>
2020-05-18 11:47:30 +02:00
13 changed files with 660 additions and 216 deletions

View File

@ -5,10 +5,17 @@ LDFLAGS=-ldflags "-X main.buildDate=${BUILD}"
.PHONY: build deps static .PHONY: build deps static
build: build:
go build ${LDFLAGS} go build ${LDFLAGS} -o ledcontroller-server cmd/ledcontroller-server/main.go
emulator:
go build ${LDFLAGS} -o emulator cmd/emulator/main.go
deps: deps:
go get cd cmd/ledcontroller-server ; go get ; cd ../..
deps-emulator:
cd cmd/emulator ; go get ; cd ../..
# Static only makes sense for the server
static: static:
CGO_ENABLED=0 GOOS=linux go build ${LDFLAGS} -a -installsuffix cgo -o LEDController . CGO_ENABLED=0 GOOS=linux go build ${LDFLAGS} -a -installsuffix cgo -o ledcontroller-server cmd/ledcontroller-server/main.go

View File

@ -1,3 +1,7 @@
# LEDController # LEDController
Testing out sending e1.31 sACM packets to strings of ws2812b Testing out sending e1.31 sACM packets to strings of ws2812b
Seems to be working nicely.
Used for my streaming setup on https://twitch.tv/iMartynOnTwitch

View File

@ -1,5 +1,5 @@
kind: pipeline kind: pipeline
type: docker type: kubernetes
name: linux-amd64-taggedver name: linux-amd64-taggedver
platform: platform:
@ -14,8 +14,8 @@ steps:
- mkdir -p /go/src/git.martyn.berlin/martyn - mkdir -p /go/src/git.martyn.berlin/martyn
- ln -s /drone/src /go/src/git.martyn.berlin/martyn/LEDController - ln -s /drone/src /go/src/git.martyn.berlin/martyn/LEDController
- cd /go/src/git.martyn.berlin/martyn/LEDController - cd /go/src/git.martyn.berlin/martyn/LEDController
- go get - make deps
- go build - make
- name: publish - name: publish
image: plugins/docker:18 image: plugins/docker:18
@ -39,7 +39,7 @@ trigger:
--- ---
kind: pipeline kind: pipeline
type: docker type: kubernetes
name: linux-amd64-devel-master name: linux-amd64-devel-master
platform: platform:
@ -54,10 +54,10 @@ steps:
- mkdir -p /go/src/git.martyn.berlin/martyn - mkdir -p /go/src/git.martyn.berlin/martyn
- ln -s /drone/src /go/src/git.martyn.berlin/martyn/LEDController - ln -s /drone/src /go/src/git.martyn.berlin/martyn/LEDController
- cd /go/src/git.martyn.berlin/martyn/LEDController - cd /go/src/git.martyn.berlin/martyn/LEDController
- go get - make deps
- go build - make
trigger: trigger:
ref: ref:
- refs/heads/devel - refs/heads/devel
- refs/heads/master - refs/heads/master

View File

@ -1,12 +1,12 @@
FROM golang@sha256:cee6f4b901543e8e3f20da3a4f7caac6ea643fd5a46201c3c2387183a332d989 AS builder FROM golang@sha256:cee6f4b901543e8e3f20da3a4f7caac6ea643fd5a46201c3c2387183a332d989 AS builder
RUN apk update && apk add --no-cache git make ca-certificates && update-ca-certificates RUN apk update && apk add --no-cache git make ca-certificates && update-ca-certificates
COPY main.go /go/src/git.martyn.berlin/martyn/LEDController/ COPY cmd /go/src/git.martyn.berlin/martyn/LEDController/cmd/
COPY internal/ /go/src/git.martyn.berlin/martyn/LEDController/internal/ COPY internal/ /go/src/git.martyn.berlin/martyn/LEDController/internal/
COPY Makefile /go/src/git.martyn.berlin/martyn/LEDController/ COPY Makefile /go/src/git.martyn.berlin/martyn/LEDController/
RUN cd /go/src/git.martyn.berlin/martyn/LEDController/; make deps ; make static RUN cd /go/src/git.martyn.berlin/martyn/LEDController/; make deps ; make static
FROM scratch FROM scratch
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=builder /go/src/git.martyn.berlin/martyn/LEDController /app/ COPY --from=builder /go/src/git.martyn.berlin/martyn/LEDController/ledcontroller-server /app/
WORKDIR /app WORKDIR /app
CMD ["/app/LEDController"] CMD ["/app/ledcontroller-server"]

48
cmd/emulator/main.go Executable file
View File

@ -0,0 +1,48 @@
package main
import (
"image/color"
"fyne.io/fyne"
"fyne.io/fyne/app"
"fyne.io/fyne/theme"
"fyne.io/fyne/widget"
fynewidget "git.martyn.berlin/martyn/LEDController/internal/fynewidget"
"log"
)
type tappableIcon struct {
widget.Icon
}
func newTappableIcon(res fyne.Resource) *tappableIcon {
icon := &tappableIcon{}
icon.ExtendBaseWidget(icon)
icon.SetResource(res)
return icon
}
func (t *tappableIcon) Tapped(_ *fyne.PointEvent) {
log.Println("I have been tapped")
}
func (t *tappableIcon) TappedSecondary(_ *fyne.PointEvent) {
}
func main() {
app := app.New()
w := app.NewWindow("Hello")
w.SetContent(widget.NewVBox(
widget.NewLabel("Hello Fyne!"),
newTappableIcon(theme.FyneLogo()),
fynewidget.NewOutputWidget(color.Black),
widget.NewButton("Quit", func() {
app.Quit()
}),
))
w.ShowAndRun()
}

219
cmd/ledcontroller-server/main.go Executable file
View File

@ -0,0 +1,219 @@
package main
import (
"fmt"
"log"
"math"
"os"
"strconv"
"time"
patterns "git.martyn.berlin/martyn/LEDController/internal/patterns"
queue "git.martyn.berlin/martyn/LEDController/internal/queue"
remapping "git.martyn.berlin/martyn/LEDController/internal/remapping"
webserver "git.martyn.berlin/martyn/LEDController/internal/webserver"
"github.com/Hundemeier/go-sacn/sacn"
)
type RGBcolor = [3]byte
var currentFrame [99][512]byte //TODO make dynamic
var channels [99]chan<- [512]byte
var sema = make(chan struct{}, 1) // a binary semaphore guarding currentFrame
var currentEffect queue.QueueItem
var globalEffectChannel = make(chan queue.QueueItem, 1024)
var universeCount int = 0
var overrideEffect queue.QueueItem
var overrideFlag = make(chan queue.QueueItem, 1)
var previousEffect queue.QueueItem
var msDelay = 0
func isColorSet(c RGBcolor) bool {
// Any color so long as it's NOT black!
return c[0] != 0 || c[1] != 0 || c[2] != 0
}
func foreverLoop() {
for /*ever*/ {
time.Sleep(time.Duration(msDelay) * time.Millisecond) //25fps
for u := 0; u < universeCount; u++ {
sema <- struct{}{} // acquire token
channels[u] <- currentFrame[u]
<-sema
}
}
}
func reduceBrightness(universe [512]byte, percentage int) [512]byte {
for i := range universe {
universe[i] = byte(float64(universe[i]) * (float64(percentage) / float64(100)))
}
return universe
}
func main() {
go func() {
var err error
listenPort := 5353
if os.Getenv("LISTEN_PORT") != "" {
listenPort, err = strconv.Atoi(os.Getenv("LISTEN_PORT"))
if err != nil {
listenPort = 5353
}
}
fmt.Printf("Starting webserver on port %d\n", listenPort)
webserver.HandleHTTP(globalEffectChannel, listenPort, overrideFlag)
}()
PanelIP := os.Getenv("PANEL_IP")
if PanelIP == "" {
PanelIP = "127.0.0.1"
}
PanelWidth, err := strconv.Atoi(os.Getenv("PANEL_WIDTH"))
if err != nil {
PanelWidth = 60
}
PanelHeight, err := strconv.Atoi(os.Getenv("PANEL_HEIGHT"))
if err != nil {
PanelHeight = 15
}
msDelay, err = strconv.Atoi(os.Getenv("MS_DELAY"))
if err != nil {
msDelay = 40
}
universeCount = int(math.Ceil(float64(PanelHeight*PanelWidth*3) / 510))
fmt.Printf("Universe count is %d\n", universeCount)
PanelBrightness, err := strconv.Atoi(os.Getenv("PANEL_BRIGHTNESS"))
if err != nil {
PanelBrightness = 100
}
//instead of "" you could provide an ip-address that the socket should bind to
trans, err := sacn.NewTransmitter("", [16]byte{1, 2}, "test")
if err != nil {
log.Fatal(err)
}
//activates the universes
for i := 0; i < universeCount; i++ {
channels[i], err = trans.Activate(uint16(i + 1))
//deactivate the channel on exit
defer close(channels[i])
trans.SetMulticast(uint16(i+1), false) //this specific setup will not multicast on windows,
trans.SetDestinations(uint16(i+1), []string{PanelIP})
}
// Plasma for start frame
/*rearranged := reduceBrightness(remapping.SliceRearrange(PanelWidth, PanelHeight, true, remapping.XYGridToLinear(PanelWidth, PanelHeight, patterns.PlasmaPanel(PanelWidth, PanelHeight, 40))), PanelBrightness)
for i := 0; i < universeCount; i++ {
currentFrame[i] = remapping.Slice512(rearranged[i])
}*/
var e queue.QueueItem
e.Effect = "red"
e.Duration = 40 * 50
globalEffectChannel <- e
e.Effect = "colour"
e.Duration = 40 * 10
e.SeedColour[0] = 0
e.SeedColour[1] = 255
e.SeedColour[2] = 0
globalEffectChannel <- e
e.Effect = "plasma"
e.Speed = 40
globalEffectChannel <- e
e.Effect = "queue"
overrideEffect = e
previousEffect.Effect = "queue"
go func() {
for /*ever*/ {
if len(overrideFlag) > 0 {
e = <-overrideFlag
if e.Effect == "queue" {
if previousEffect.Effect != "queue" {
fmt.Printf("Returning to queue : %s\n", previousEffect.Effect)
overrideEffect = e
currentEffect = previousEffect
previousEffect.Effect = "queue"
currentEffect = <-globalEffectChannel
}
} else {
fmt.Printf("Overriding with : %s\n", e.Effect)
if previousEffect.Effect == "queue" {
previousEffect = currentEffect
}
overrideEffect = e
currentEffect = e
}
}
if overrideEffect.Effect == "queue" {
if currentEffect.Duration > 0 {
if currentEffect.Duration%uint64(msDelay) != 0 {
currentEffect.Duration = uint64(currentEffect.Duration/uint64(msDelay)) * uint64(msDelay)
}
currentEffect.Duration -= uint64(msDelay)
} else {
if len(globalEffectChannel) > 0 {
previousEffect.Effect = "queue"
lastEffect := currentEffect
currentEffect = <-globalEffectChannel
if lastEffect != currentEffect {
fmt.Printf("New effect selected : %s\n", currentEffect.Effect)
}
}
}
}
var rearranged [][]byte
switch currentEffect.Effect {
case "line":
rearranged = remapping.SliceRearrange(PanelWidth, PanelHeight, true, remapping.XYGridToLinear(PanelWidth, PanelHeight, patterns.ZigZag(PanelWidth, PanelHeight)))
case "plasma":
if isColorSet(currentEffect.SeedColour) {
rearranged = remapping.SliceRearrange(PanelWidth, PanelHeight, true, remapping.XYGridToLinear(PanelWidth, PanelHeight, patterns.PlasmaPanelSingleColor(PanelWidth, PanelHeight, currentEffect.Speed, currentEffect.SeedColour)))
} else {
rearranged = remapping.SliceRearrange(PanelWidth, PanelHeight, true, remapping.XYGridToLinear(PanelWidth, PanelHeight, patterns.PlasmaPanel(PanelWidth, PanelHeight, currentEffect.Speed)))
}
case "red":
rearranged = remapping.SliceRearrange(PanelWidth, PanelHeight, false, remapping.XYGridToLinear(PanelWidth, PanelHeight, patterns.RedPanel(PanelWidth, PanelHeight)))
case "random":
rearranged = remapping.SliceRearrange(PanelWidth, PanelHeight, false, remapping.XYGridToLinear(PanelWidth, PanelHeight, patterns.RandomColourPanel(PanelWidth, PanelHeight, currentEffect.Speed)))
case "linearplasma":
rearranged = remapping.SliceRearrange(PanelWidth, PanelHeight, false, patterns.LinearPlasma(PanelWidth*PanelHeight))
case "gradientred":
rearranged = remapping.SliceRearrange(PanelWidth, PanelHeight, false, patterns.Gradient(255, 0, 0, 0, 0, 255, 0, PanelWidth*PanelHeight))
case "sine":
var black patterns.RGBcolor
var red patterns.RGBcolor
red[0] = 255
rearranged = remapping.SliceRearrange(PanelWidth, PanelHeight, true, remapping.XYGridToLinear(PanelWidth, PanelHeight, patterns.Sinewave(PanelWidth, PanelHeight, black, red, currentEffect.Speed)))
case "sinechase":
var black patterns.RGBcolor
var red patterns.RGBcolor
red[0] = 255
rearranged = remapping.SliceRearrange(PanelWidth, PanelHeight, true, remapping.XYGridToLinear(PanelWidth, PanelHeight, patterns.SineChase(PanelWidth, PanelHeight, black, red, currentEffect.Speed)))
case "plasmapulse":
rearranged = remapping.SliceRearrange(PanelWidth, PanelHeight, false, remapping.XYGridToLinear(PanelWidth, PanelHeight, patterns.PlasmaColourPanel(PanelWidth, PanelHeight, currentEffect.Speed)))
case "colour":
rearranged = remapping.SliceRearrange(PanelWidth, PanelHeight, false, remapping.XYGridToLinear(PanelWidth, PanelHeight, patterns.FillPanel(PanelWidth, PanelHeight, currentEffect.SeedColour[0], currentEffect.SeedColour[1], currentEffect.SeedColour[2])))
case "fade":
rearranged = remapping.SliceRearrange(PanelWidth, PanelHeight, true, remapping.XYGridToLinear(PanelWidth, PanelHeight, patterns.GradientPanel(PanelWidth, PanelHeight, currentEffect.SeedColour, currentEffect.SecondColour)))
default:
rearranged = remapping.SliceRearrange(PanelWidth, PanelHeight, false, remapping.XYGridToLinear(PanelWidth, PanelHeight, patterns.FillPanel(PanelWidth, PanelHeight, 128, 0, 128)))
}
//rearranged := remapping.SliceRearrange(PanelWidth,PanelHeight,true,linearPlasma(PanelWidth*PanelHeight))
sema <- struct{}{} // acquire token
for i := 0; i < universeCount; i++ {
currentFrame[i] = reduceBrightness(remapping.Slice512(rearranged[i]), PanelBrightness)
}
<-sema
time.Sleep(40 * time.Millisecond)
}
}()
foreverLoop()
}

View File

@ -35,7 +35,7 @@ spec:
- name: PANEL_BRIGHTNESS - name: PANEL_BRIGHTNESS
value: "10" value: "10"
image: imartyn/ledcontroller:0.0-linux-amd64 image: imartyn/ledcontroller:0.0-linux-amd64
imagePullPolicy: IfNotPresent imagePullPolicy: Always
name: ledcontroller name: ledcontroller
ports: ports:
- name: web - name: web

View File

@ -0,0 +1,45 @@
package fynewidget
import (
"image/color"
"fyne.io/fyne/widget"
"fyne.io/fyne"
)
// OutputWidget describes a coloured rectangle primitive in a Fyne canvas
type OutputWidget struct {
widget.BaseWidget
CreateRenderer() WidgetRenderer
}
// NewOutputWidget returns a new OutputWidget instance
func NewOutputWidget(color color.Color) *OutputWidget {
return &OutputWidget{}
}
// MinSize of panel
func (l *OutputWidget) MinSize() fyne.Size {
return fyne.NewSize(320, 200)
}
// CreateRenderer gets the widget renderer
func (t *Table) CreateRenderer() fyne.WidgetRenderer {
return widget.new
}
/*
// NewOutputWidget Creates a new widget for outputting the patterns recieved over UDP
func NewOutputWidget() *fyne.Container {
theBox := NewOutputWidget(theme.PrimaryColor())
theBox.Resize(fyne.NewSize(320, 200))
c := fyne.NewContainer(theBox)
c.Resize(fyne.NewSize(320, 200))
return c
}
*/

138
internal/patterns/font.go Executable file
View File

@ -0,0 +1,138 @@
package patterns
var FontData = [126][]byte{{0x00, 0x00, 0x00},
{0x00, 0x00, 0x00},
{0x00, 0x00, 0x00},
{0x00, 0x00, 0x00},
{0x00, 0x00, 0x00},
{0x00, 0x00, 0x00},
{0x00, 0x00, 0x00},
{0x00, 0x00, 0x00},
{0x00, 0x00, 0x00},
{0x00, 0x00, 0x00},
{0x00, 0x00, 0x00},
{0x00, 0x00, 0x00},
{0x00, 0x00, 0x00},
{0x00, 0x00, 0x00},
{0x00, 0x00, 0x00},
{0x00, 0x00, 0x00},
{0x00, 0x00, 0x00},
{0x00, 0x00, 0x00},
{0x00, 0x00, 0x00},
{0x00, 0x00, 0x00},
{0x00, 0x00, 0x00},
{0x00, 0x00, 0x00},
{0x00, 0x00, 0x00},
{0x00, 0x00, 0x00},
{0x00, 0x00, 0x00},
{0x00, 0x00, 0x00},
{0x00, 0x00, 0x00},
{0x00, 0x00, 0x00},
{0x00, 0x00, 0x00},
{0x00, 0x00, 0x00},
{0x00, 0x00, 0x00}, // 32 ' '
{0x5f}, // 33 '!'
{0x07, 0x00, 0x07}, // 34 '"'
{0x14, 0x3e, 0x14, 0x3e, 0x14}, // 35 '#'
{0x24, 0x4a, 0xcb, 0x52, 0x24}, // 36 '$'
{0x22, 0x15, 0x2a, 0x54, 0x22}, // 37 '%'
{0x36, 0x49, 0x55, 0x22, 0x50}, // 38 '&'
{0x07}, // 39 '''
{0x1c, 0x22, 0x41}, // 40 '('
{0x41, 0x22, 0x1c}, // 41 ')'
{0x14, 0x08, 0x3e, 0x08, 0x14}, // 42 '*'
{0x08, 0x08, 0x3e, 0x08, 0x08}, // 43 '+'
{0xc0, 0x60}, // 44 ','
{0x08, 0x08, 0x08}, // 45 '-'
{0x60, 0x60}, // 46 '.'
{0x20, 0x10, 0x08, 0x04, 0x02}, // 47 '/'
{0x3e, 0x51, 0x49, 0x45, 0x3e}, // 48 '0'
{0x42, 0x42, 0x7f, 0x40, 0x40}, // 49 '1'
{0x42, 0x61, 0x51, 0x49, 0x46}, // 50 '2'
{0x22, 0x41, 0x49, 0x49, 0x36}, // 51 '3'
{0x18, 0x14, 0x52, 0x7f, 0x50}, // 52 '4'
{0x27, 0x45, 0x45, 0x45, 0x39}, // 53 '5'
{0x3e, 0x45, 0x45, 0x45, 0x38}, // 54 '6'
{0x01, 0x01, 0x71, 0x09, 0x07}, // 55 '7'
{0x36, 0x49, 0x49, 0x49, 0x36}, // 56 '8'
{0x0e, 0x51, 0x51, 0x51, 0x3e}, // 57 '9'
{0x66, 0x66}, // 58 ':'
{0xc6, 0x66}, // 59 ';'
{0x08, 0x14, 0x22, 0x41}, // 60 '<'
{0x14, 0x14, 0x14, 0x14, 0x14}, // 61 '='
{0x41, 0x22, 0x14, 0x08}, // 62 '>'
{0x02, 0x01, 0x51, 0x09, 0x06}, // 63 '?'
{0x3e, 0x41, 0x5d, 0x55, 0x5e}, // 64 '@'
{0x7c, 0x12, 0x11, 0x12, 0x7c}, // 65 'A'
{0x7f, 0x49, 0x49, 0x49, 0x36}, // 66 'B'
{0x3e, 0x41, 0x41, 0x41, 0x22}, // 67 'C'
{0x7f, 0x41, 0x41, 0x41, 0x3e}, // 68 'D'
{0x7f, 0x49, 0x49, 0x49, 0x41}, // 69 'E'
{0x7f, 0x09, 0x09, 0x09, 0x01}, // 70 'F'
{0x3e, 0x41, 0x41, 0x49, 0x3a}, // 71 'G'
{0x7f, 0x08, 0x08, 0x08, 0x7f}, // 72 'H'
{0x41, 0x41, 0x7f, 0x41, 0x41}, // 73 'I'
{0x30, 0x40, 0x40, 0x40, 0x3f}, // 74 'J'
{0x7f, 0x08, 0x14, 0x22, 0x41}, // 75 'K'
{0x7f, 0x40, 0x40, 0x40, 0x40}, // 76 'L'
{0x7f, 0x02, 0x0c, 0x02, 0x7f}, // 77 'M'
{0x7f, 0x02, 0x04, 0x08, 0x7f}, // 78 'N'
{0x3e, 0x41, 0x41, 0x41, 0x3e}, // 79 'O'
{0x7f, 0x09, 0x09, 0x09, 0x06}, // 80 'P'
{0x3e, 0x41, 0x61, 0x41, 0xbe}, // 81 'Q'
{0x7f, 0x09, 0x09, 0x09, 0x76}, // 82 'R'
{0x26, 0x49, 0x49, 0x49, 0x32}, // 83 'S'
{0x01, 0x01, 0x7f, 0x01, 0x01}, // 84 'T'
{0x3f, 0x40, 0x40, 0x40, 0x3f}, // 85 'U'
{0x07, 0x18, 0x60, 0x18, 0x07}, // 86 'V'
{0x7f, 0x20, 0x18, 0x20, 0x7f}, // 87 'W'
{0x63, 0x14, 0x08, 0x14, 0x63}, // 88 'X'
{0x07, 0x08, 0x70, 0x08, 0x07}, // 89 'Y'
{0x61, 0x51, 0x49, 0x45, 0x43}, // 90 'Z'
{0x7f, 0x41}, // 91 '['
{0x02, 0x04, 0x08, 0x10, 0x20}, // 92 '\'
{0x41, 0x7f}, // 93 ']'
{0x04, 0x02, 0x01, 0x02, 0x04}, // 94 '^'
{0x80, 0x80, 0x80, 0x80, 0x80}, // 95 '_'
{0x01, 0x02}, // 96 '`'
{0x20, 0x54, 0x54, 0x78}, // 97 'a'
{0x7f, 0x44, 0x44, 0x38}, // 98 'b'
{0x38, 0x44, 0x44, 0x28}, // 99 'c'
{0x38, 0x44, 0x44, 0x7f}, // 100 'd'
{0x38, 0x54, 0x54, 0x58}, // 101 'e'
{0x08, 0x7e, 0x09, 0x02}, // 102 'f'
{0x18, 0xa4, 0xa4, 0x7c}, // 103 'g'
{0x7f, 0x04, 0x04, 0x78}, // 104 'h'
{0x44, 0x7d, 0x40}, // 105 'i'
{0x84, 0x7d}, // 106 'j'
{0x7f, 0x10, 0x28, 0x44}, // 107 'k'
{0x41, 0x7f, 0x40}, // 108 'l'
{0x7c, 0x04, 0x7c, 0x04, 0x78}, // 109 'm'
{0x7c, 0x08, 0x04, 0x78}, // 110 'n'
{0x38, 0x44, 0x44, 0x38}, // 111 'o'
{0xfc, 0x24, 0x24, 0x18}, // 112 'p'
{0x18, 0x24, 0x24, 0xfc}, // 113 'q'
{0x7c, 0x08, 0x04, 0x08}, // 114 'r'
{0x48, 0x54, 0x54, 0x24}, // 115 's'
{0x04, 0x3f, 0x44}, // 116 't'
{0x3c, 0x40, 0x20, 0x7c}, // 117 'u'
{0x3c, 0x40, 0x3c}, // 118 'v'
{0x3c, 0x40, 0x3c, 0x40, 0x3c}, // 119 'w'
{0x6c, 0x10, 0x6c}, // 120 'x'
{0x1c, 0xa0, 0xa0, 0x7c}, // 121 'y'
{0x64, 0x54, 0x4c}, // 122 'z'
{0x08, 0x36, 0x41}, // 123 '{'
{0x7f}, // 124 '|'
{0x41, 0x36, 0x08}, // 125 '}'
{0x08, 0x04, 0x08, 0x04}} // 126 '~'
func Letter(whichLetter rune) [][]RGBcolor {
w := 8
h := 8
grid := make([][]RGBcolor, w)
for i := 0; i < w; i++ {
grid[i] = make([]RGBcolor, h)
}
//index := int(whichLetter)
return grid
}

View File

@ -1,9 +1,15 @@
package patterns package patterns
import "math" import (
"math"
)
type RGBcolor = [3]byte type RGBcolor = [3]byte
func floatColorToIntColor(val float64) byte {
return byte(val*256 - 0.5)
}
func plasmaRGBFromVal(val byte) (byte, byte, byte) { func plasmaRGBFromVal(val byte) (byte, byte, byte) {
var r byte var r byte
var g byte var g byte
@ -24,7 +30,6 @@ func plasmaRGBFromVal(val byte) (byte, byte, byte) {
return r, g, b return r, g, b
} }
func LinearPlasma(l int) []byte { func LinearPlasma(l int) []byte {
line := make([]byte, l*3) line := make([]byte, l*3)
for i := 0; i < l*3; i += 3 { for i := 0; i < l*3; i += 3 {
@ -37,26 +42,67 @@ func LinearPlasma(l int) []byte {
return line return line
} }
var offset float64 = 0.5 func linearFromVal(val byte) (byte, byte, byte) {
var r byte = val
var g byte = 0
var b byte = 0
return r, g, b
}
func positiveModF(val float64) float64 {
_, val = math.Modf(val)
if val < 0 {
return val + 1.0
}
return val
}
func colorPlasmaFromFloatVal(val float64) (byte, byte, byte) {
var r byte
var g byte
var b byte
val = positiveModF(val) * 3.0
if val < 1.0 {
r = floatColorToIntColor(val)
g = floatColorToIntColor(1.0 - val)
b = 0
} else if val < 2.0 {
b = floatColorToIntColor(val - 1.0)
r = floatColorToIntColor(1.0 - (val - 1.0))
g = 0.0
} else {
g = floatColorToIntColor(val - 2.0)
b = floatColorToIntColor(1.0 - (val - 2.0))
r = 0.0
}
return r, g, b
}
var palletteOffset float64 = 0.0
func PlasmaPanel(w int, h int, speed uint16) [][]RGBcolor { func PlasmaPanel(w int, h int, speed uint16) [][]RGBcolor {
grid := make([][]RGBcolor, w) grid := make([][]RGBcolor, w)
for i := 0; i < w; i++ { for i := 0; i < w; i++ {
grid[i] = make([]RGBcolor, h) grid[i] = make([]RGBcolor, h)
} }
scale := math.Pi * 2.0 / float64(w)
step := float64(5 * speed / 40) // pbrook didn't say what DT is for... so it resolves to 0.05
offset -= step palletteOffset -= 0.05 / 1.0
if offset < 0 { if palletteOffset < 0 {
offset += 1.0 palletteOffset += 1.0
} }
offset := 0.5 // center offset
scaleW := math.Pi * 2.0 / float64(w)
scaleH := math.Pi * 2.0 / float64(h)
for y := 0; y < h; y++ { for y := 0; y < h; y++ {
for x := 0; x < w; x++ { for x := 0; x < w; x++ {
u := math.Cos((float64(x) + offset) * scale) u := math.Cos((float64(x) + offset) * scaleW)
v := math.Cos((float64(y) + offset) * scale) v := math.Cos((float64(y) + offset) * scaleH)
w := math.Cos((float64(w) + offset) * scale) j := math.Cos(offset * scaleW) // 2D - No Z
e := (u + v + w + 3.0) / 6.0 e := (u + v + j + 3.0) / 6.0
r, g, b := plasmaRGBFromVal(byte((offset + e) * 255)) r, g, b := colorPlasmaFromFloatVal(palletteOffset + e)
var rgb [3]byte var rgb [3]byte
rgb[0] = r rgb[0] = r
rgb[1] = g rgb[1] = g
@ -65,4 +111,47 @@ func PlasmaPanel(w int, h int, speed uint16) [][]RGBcolor {
} }
} }
return grid return grid
} }
func singleColorPlasmaFromFloatVal(val float64, baseColor RGBcolor) (byte, byte, byte) {
var r byte
var g byte
var b byte
val = positiveModF(val)
r = byte(math.Trunc(float64(baseColor[0]) * val))
g = byte(math.Trunc(float64(baseColor[1]) * val))
b = byte(math.Trunc(float64(baseColor[2]) * val))
return r, g, b
}
func PlasmaPanelSingleColor(w int, h int, speed uint16, baseColor RGBcolor) [][]RGBcolor {
grid := make([][]RGBcolor, w)
for i := 0; i < w; i++ {
grid[i] = make([]RGBcolor, h)
}
palletteOffset -= 0.05 / 1.0
if palletteOffset < 0 {
palletteOffset += 1.0
}
offset := 0.5 // center offset
scaleW := math.Pi * 2.0 / float64(w)
scaleH := math.Pi * 2.0 / float64(h)
for y := 0; y < h; y++ {
for x := 0; x < w; x++ {
u := math.Cos((float64(x) + offset) * scaleW)
v := math.Cos((float64(y) + offset) * scaleH)
j := math.Cos(offset * scaleW) // 2D - No Z
e := (u + v + j + 3.0) / 6.0
r, g, b := singleColorPlasmaFromFloatVal(palletteOffset+e, baseColor)
var rgb [3]byte
rgb[0] = r
rgb[1] = g
rgb[2] = b
grid[x][y] = rgb
}
}
return grid
}

View File

@ -3,9 +3,9 @@ package queue
type RGBcolor = [3]byte type RGBcolor = [3]byte
type QueueItem struct { type QueueItem struct {
Effect string Effect string
Duration uint16 Duration uint64
Speed uint16 //only used by some patterns Speed uint16 //only used by some patterns
SeedColour RGBcolor // only used by some patterns SeedColour RGBcolor // only used by some patterns
SecondColour RGBcolor // only used by some patterns SecondColour RGBcolor // only used by some patterns
} }

View File

@ -1,18 +1,17 @@
package webserver package webserver
import ( import (
queue "git.martyn.berlin/martyn/LEDController/internal/queue"
"github.com/gorilla/handlers" "github.com/gorilla/handlers"
"github.com/gorilla/mux" "github.com/gorilla/mux"
queue "git.martyn.berlin/martyn/LEDController/internal/queue"
"golang.org/x/image/colornames" "golang.org/x/image/colornames"
"fmt" "fmt"
"net/http" "net/http"
"os" "os"
"time"
"strconv" "strconv"
"strings" "strings"
"time"
) )
func HealthHandler(response http.ResponseWriter, request *http.Request) { func HealthHandler(response http.ResponseWriter, request *http.Request) {
@ -30,7 +29,8 @@ func RootHandler(response http.ResponseWriter, request *http.Request) {
fmt.Fprint(response, "Not implemented") fmt.Fprint(response, "Not implemented")
} }
var globalQueue chan queue.QueueItem; var globalQueue chan queue.QueueItem
var overrideFlag chan queue.QueueItem
func PatternHandler(response http.ResponseWriter, request *http.Request) { func PatternHandler(response http.ResponseWriter, request *http.Request) {
vars := mux.Vars(request) vars := mux.Vars(request)
@ -39,10 +39,14 @@ func PatternHandler(response http.ResponseWriter, request *http.Request) {
e.Effect = strings.ToLower(vars["pattern"]) e.Effect = strings.ToLower(vars["pattern"])
_, found := vars["duration"] _, found := vars["duration"]
if found { if found {
i, _ := strconv.Atoi(vars["duration"]) i, _ := strconv.ParseUint(vars["duration"], 10, 64)
e.Duration = uint16(i) e.Duration = i
} else { } else {
e.Duration = 5000 if vars["override"] == "true" {
e.Duration = 0
} else {
e.Duration = 5000
}
} }
_, found = vars["speed"] _, found = vars["speed"]
if found { if found {
@ -51,24 +55,44 @@ func PatternHandler(response http.ResponseWriter, request *http.Request) {
} else { } else {
e.Speed = 40 e.Speed = 40
} }
globalQueue <- e if vars["color"] != "" {
var c queue.RGBcolor
for cn, cv := range colornames.Map {
if cn == strings.ToLower(vars["color"]) {
c[0] = cv.R
c[1] = cv.G
c[2] = cv.B
}
}
e.SeedColour = c
}
if vars["override"] == "true" {
overrideFlag <- e
} else {
globalQueue <- e
}
fmt.Fprint(response, "OKAY") fmt.Fprint(response, "OKAY")
} }
func ColourHandler(response http.ResponseWriter, request *http.Request) { func ColourHandler(response http.ResponseWriter, request *http.Request) {
vars := mux.Vars(request) vars := mux.Vars(request)
fmt.Printf("Vars : %v\n", vars)
response.Header().Add("Content-type", "text/plain") response.Header().Add("Content-type", "text/plain")
var e queue.QueueItem var e queue.QueueItem
e.Effect = "colour" e.Effect = "colour"
_, found := vars["duration"] _, found := vars["duration"]
if found { if found {
i, _ := strconv.Atoi(vars["duration"]) i, _ := strconv.ParseUint(vars["duration"], 10, 64)
e.Duration = uint16(i) e.Duration = i
} else { } else {
e.Duration = 5000 if vars["override"] == "true" {
e.Duration = 0
} else {
e.Duration = 5000
}
} }
var c queue.RGBcolor var c queue.RGBcolor
for cn, cv := range(colornames.Map) { for cn, cv := range colornames.Map {
if cn == strings.ToLower(vars["name"]) { if cn == strings.ToLower(vars["name"]) {
c[0] = cv.R c[0] = cv.R
c[1] = cv.G c[1] = cv.G
@ -77,6 +101,11 @@ func ColourHandler(response http.ResponseWriter, request *http.Request) {
} }
e.SeedColour = c e.SeedColour = c
globalQueue <- e globalQueue <- e
if vars["override"] == "true" {
overrideFlag <- e
} else {
globalQueue <- e
}
fmt.Fprint(response, "OKAY") fmt.Fprint(response, "OKAY")
} }
@ -87,13 +116,17 @@ func FadeHandler(response http.ResponseWriter, request *http.Request) {
e.Effect = "fade" e.Effect = "fade"
_, found := vars["duration"] _, found := vars["duration"]
if found { if found {
i, _ := strconv.Atoi(vars["duration"]) i, _ := strconv.ParseUint(vars["duration"], 10, 64)
e.Duration = uint16(i) e.Duration = i
} else { } else {
e.Duration = 5000 if vars["override"] == "true" {
e.Duration = 0
} else {
e.Duration = 5000
}
} }
var c queue.RGBcolor var c queue.RGBcolor
for cn, cv := range(colornames.Map) { for cn, cv := range colornames.Map {
if cn == strings.ToLower(vars["namefrom"]) { if cn == strings.ToLower(vars["namefrom"]) {
c[0] = cv.R c[0] = cv.R
c[1] = cv.G c[1] = cv.G
@ -101,7 +134,7 @@ func FadeHandler(response http.ResponseWriter, request *http.Request) {
} }
} }
e.SeedColour = c e.SeedColour = c
for cn, cv := range(colornames.Map) { for cn, cv := range colornames.Map {
if cn == strings.ToLower(vars["nameto"]) { if cn == strings.ToLower(vars["nameto"]) {
c[0] = cv.R c[0] = cv.R
c[1] = cv.G c[1] = cv.G
@ -109,30 +142,52 @@ func FadeHandler(response http.ResponseWriter, request *http.Request) {
} }
} }
e.SecondColour = c e.SecondColour = c
globalQueue <- e if vars["override"] == "true" {
overrideFlag <- e
} else {
globalQueue <- e
}
fmt.Fprint(response, "OKAY") fmt.Fprint(response, "OKAY")
} }
func HandleHTTP(queueChannel chan queue.QueueItem, Port int) { func ResumeHandler(response http.ResponseWriter, request *http.Request) {
response.Header().Add("Content-type", "text/plain")
var e queue.QueueItem
e.Effect = "queue"
overrideFlag <- e
fmt.Fprint(response, "OKAY")
}
func HandleHTTP(queueChannel chan queue.QueueItem, Port int, overrideChannel chan queue.QueueItem) {
globalQueue = queueChannel globalQueue = queueChannel
overrideFlag = overrideChannel
r := mux.NewRouter() r := mux.NewRouter()
loggedRouter := handlers.LoggingHandler(os.Stdout, r) loggedRouter := handlers.LoggingHandler(os.Stdout, r)
r.NotFoundHandler = http.HandlerFunc(NotFoundHandler) r.NotFoundHandler = http.HandlerFunc(NotFoundHandler)
r.HandleFunc("/", RootHandler) r.HandleFunc("/", RootHandler)
r.HandleFunc("/healthz", HealthHandler) r.HandleFunc("/healthz", HealthHandler)
r.HandleFunc("/pattern/{pattern}", PatternHandler) r.HandleFunc("/pattern/{pattern}/override={override}", PatternHandler)
r.HandleFunc("/pattern/{pattern}/", PatternHandler)
r.HandleFunc("/pattern/{pattern}/{duration}", PatternHandler) r.HandleFunc("/pattern/{pattern}/{duration}", PatternHandler)
r.HandleFunc("/pattern/{pattern}/{duration}/{speed}", PatternHandler) r.HandleFunc("/pattern/{pattern}/{duration}/{speed}", PatternHandler)
r.HandleFunc("/colorpattern/{color}/{pattern}/override={override}", PatternHandler)
r.HandleFunc("/colorpattern/{color}/{pattern}/", PatternHandler)
r.HandleFunc("/colorpattern/{color}/{pattern}/{duration}", PatternHandler)
r.HandleFunc("/colorpattern/{color}/{pattern}/{duration}/{speed}", PatternHandler)
r.HandleFunc("/colour/{name}", ColourHandler) r.HandleFunc("/colour/{name}", ColourHandler)
r.HandleFunc("/colour/{name}/override={override}", ColourHandler)
r.HandleFunc("/colour/{name}/{duration}", ColourHandler) r.HandleFunc("/colour/{name}/{duration}", ColourHandler)
r.HandleFunc("/color/{name}", ColourHandler) r.HandleFunc("/color/{name}", ColourHandler)
r.HandleFunc("/color/{name}/override={override}", ColourHandler)
r.HandleFunc("/color/{name}/{duration}", ColourHandler) r.HandleFunc("/color/{name}/{duration}", ColourHandler)
r.HandleFunc("/fade/{namefrom}/{nameto}", FadeHandler) r.HandleFunc("/fade/{namefrom}/{nameto}", FadeHandler)
r.HandleFunc("/fade/{namefrom}/{nameto}/override={override}", FadeHandler)
r.HandleFunc("/fade/{namefrom}/{nameto}/{duration}", FadeHandler) r.HandleFunc("/fade/{namefrom}/{nameto}/{duration}", FadeHandler)
r.HandleFunc("/resumequeue", ResumeHandler)
http.Handle("/", r) http.Handle("/", r)
srv := &http.Server{ srv := &http.Server{
Handler: loggedRouter, Handler: loggedRouter,
Addr: "0.0.0.0:"+strconv.Itoa(Port), Addr: "0.0.0.0:" + strconv.Itoa(Port),
WriteTimeout: 15 * time.Second, WriteTimeout: 15 * time.Second,
ReadTimeout: 15 * time.Second, ReadTimeout: 15 * time.Second,
} }

161
main.go
View File

@ -1,161 +0,0 @@
package main
import (
"fmt"
"log"
"time"
"strconv"
"os"
webserver "git.martyn.berlin/martyn/LEDController/internal/webserver"
queue "git.martyn.berlin/martyn/LEDController/internal/queue"
remapping "git.martyn.berlin/martyn/LEDController/internal/remapping"
patterns "git.martyn.berlin/martyn/LEDController/internal/patterns"
"github.com/Hundemeier/go-sacn/sacn"
)
type RGBcolor = [3]byte
var currentFrame [2][512]byte
var channels [2]chan<-[512]byte
var sema = make(chan struct{}, 1) // a binary semaphore guarding currentFrame
var currentEffect queue.QueueItem;
var globalEffectChannel = make(chan queue.QueueItem, 1024)
func foreverLoop() {
for /*ever*/ {
time.Sleep(40 * time.Millisecond) //25fps
for u := 0; u < 2; u++ {
sema <- struct{}{} // acquire token
channels[u] <- currentFrame[u]
<- sema
}
}
}
func reduceBrightness(universe [512]byte, percentage int) [512]byte{
for i := range(universe) {
universe[i] = byte(float64(universe[i]) * (float64(percentage) / float64(100)))
}
return universe
}
func main() {
go func() {
var err error
listenPort := 5353
if os.Getenv("LISTEN_PORT") != "" {
listenPort, err = strconv.Atoi(os.Getenv("LISTEN_PORT"))
if err != nil {
listenPort = 5353
}
}
fmt.Printf("Starting webserver on port %d\n", listenPort)
webserver.HandleHTTP(globalEffectChannel, listenPort)
}()
PanelIP := os.Getenv("PANEL_IP")
if PanelIP == "" {
PanelIP = "127.0.0.1"
}
PanelWidth, err := strconv.Atoi(os.Getenv("PANEL_WIDTH"))
if err != nil {
PanelWidth = 68
}
PanelHeight, err := strconv.Atoi(os.Getenv("PANEL_HEIGHT"))
if err != nil {
PanelHeight = 4
}
PanelBrightness, err := strconv.Atoi(os.Getenv("PANEL_BRIGHTNESS"))
if err != nil {
PanelBrightness = 100
}
//instead of "" you could provide an ip-address that the socket should bind to
trans, err := sacn.NewTransmitter("", [16]byte{1, 2}, "test")
if err != nil {
log.Fatal(err)
}
//activates the universes
for i := 0; i < 2; i++ {
channels[i], err = trans.Activate(uint16(i+1))
//deactivate the channel on exit
defer close(channels[i])
trans.SetMulticast(uint16(i+1), false) //this specific setup will not multicast on windows,
trans.SetDestinations(uint16(i+1), []string{PanelIP})
}
// Plasma for start frame
rearranged := remapping.SliceRearrange(PanelWidth, PanelHeight, true, remapping.XYGridToLinear(PanelWidth, PanelHeight, patterns.PlasmaPanel(PanelWidth, PanelHeight, 40)))
currentFrame[0] = remapping.Slice512(rearranged[0])
currentFrame[1] = remapping.Slice512(rearranged[1])
var e queue.QueueItem
e.Effect = "red"
e.Duration = 40 * 50
globalEffectChannel <- e
e.Effect = "colour"
e.Duration = 40 * 10
e.SeedColour[0] = 0
e.SeedColour[1] = 255
e.SeedColour[2] = 0
globalEffectChannel <- e
e.Effect = "default"
globalEffectChannel <- e
go func() {
for /*ever*/ {
if currentEffect.Duration > 0 {
currentEffect.Duration -= 40
} else {
if len(globalEffectChannel) > 0 {
currentEffect = <- globalEffectChannel
}
}
var rearranged [][]byte
switch currentEffect.Effect {
case "line":
rearranged = remapping.SliceRearrange(PanelWidth, PanelHeight, true, remapping.XYGridToLinear(PanelWidth, PanelHeight, patterns.ZigZag(PanelWidth, PanelHeight)))
case "plasma":
rearranged = remapping.SliceRearrange(PanelWidth, PanelHeight, true, remapping.XYGridToLinear(PanelWidth, PanelHeight, patterns.PlasmaPanel(PanelWidth, PanelHeight, currentEffect.Speed)))
case "red":
rearranged = remapping.SliceRearrange(PanelWidth, PanelHeight, false, remapping.XYGridToLinear(PanelWidth, PanelHeight, patterns.RedPanel(PanelWidth,PanelHeight)))
case "random":
rearranged = remapping.SliceRearrange(PanelWidth, PanelHeight, false, remapping.XYGridToLinear(PanelWidth, PanelHeight, patterns.RandomColourPanel(PanelWidth,PanelHeight,currentEffect.Speed)))
case "linearplasma":
rearranged = remapping.SliceRearrange(PanelWidth, PanelHeight, false, patterns.LinearPlasma(PanelWidth*PanelHeight))
case "gradientred":
rearranged = remapping.SliceRearrange(PanelWidth, PanelHeight, false, patterns.Gradient(255, 0, 0, 0, 0, 255, 0, PanelWidth*PanelHeight))
case "sine":
var black patterns.RGBcolor
var red patterns.RGBcolor
red[0] = 255
rearranged = remapping.SliceRearrange(PanelWidth, PanelHeight, true, remapping.XYGridToLinear(PanelWidth, PanelHeight, patterns.Sinewave(PanelWidth, PanelHeight, black, red, currentEffect.Speed)))
case "sinechase":
var black patterns.RGBcolor
var red patterns.RGBcolor
red[0] = 255
rearranged = remapping.SliceRearrange(PanelWidth, PanelHeight, true, remapping.XYGridToLinear(PanelWidth, PanelHeight, patterns.SineChase(PanelWidth, PanelHeight, black, red, currentEffect.Speed)))
case "plasmapulse":
rearranged = remapping.SliceRearrange(PanelWidth, PanelHeight, false, remapping.XYGridToLinear(PanelWidth, PanelHeight, patterns.PlasmaColourPanel(PanelWidth,PanelHeight, currentEffect.Speed)))
case "colour":
rearranged = remapping.SliceRearrange(PanelWidth, PanelHeight, false, remapping.XYGridToLinear(PanelWidth, PanelHeight, patterns.FillPanel(PanelWidth,PanelHeight, currentEffect.SeedColour[0], currentEffect.SeedColour[1], currentEffect.SeedColour[2])))
case "fade":
rearranged = remapping.SliceRearrange(PanelWidth, PanelHeight, true, remapping.XYGridToLinear(PanelWidth, PanelHeight, patterns.GradientPanel(PanelWidth,PanelHeight, currentEffect.SeedColour, currentEffect.SecondColour)))
default:
rearranged = remapping.SliceRearrange(PanelWidth, PanelHeight, false, remapping.XYGridToLinear(PanelWidth, PanelHeight, patterns.FillPanel(PanelWidth,PanelHeight, 128,0,128)))
}
//rearranged := remapping.SliceRearrange(PanelWidth,PanelHeight,true,linearPlasma(PanelWidth*PanelHeight))
sema <- struct{}{} // acquire token
currentFrame[0] = reduceBrightness(remapping.Slice512(rearranged[0]),PanelBrightness)
currentFrame[1] = reduceBrightness(remapping.Slice512(rearranged[1]),PanelBrightness)
<- sema
time.Sleep(40 * time.Millisecond)
}
}()
foreverLoop();
}