Files
cheesy-arena-lite/led/strip.go
2018-07-21 22:28:42 -07:00

390 lines
10 KiB
Go

// Copyright 2018 Team 254. All Rights Reserved.
// Author: pat@patfairbank.com (Patrick Fairbank)
//
// Represents an independently controlled LED strip making up one half of a scale or switch LED array.
package led
import (
"math/rand"
"time"
)
type strip struct {
currentMode Mode
isRed bool
pixels [numPixels][3]byte
oldPixels [numPixels][3]byte
counter int
lastPacketTime time.Time
}
// Calculates the current pixel values depending on the mode and elapsed counter cycles.
func (strip *strip) updatePixels() {
switch strip.currentMode {
case RedMode:
strip.updateSingleColorMode(red)
case GreenMode:
strip.updateSingleColorMode(green)
case BlueMode:
strip.updateSingleColorMode(blue)
case WhiteMode:
strip.updateSingleColorMode(white)
case ChaseMode:
strip.updateChaseMode()
case WarmupMode:
strip.updateWarmupMode()
case Warmup2Mode:
strip.updateWarmup2Mode()
case Warmup3Mode:
strip.updateWarmup3Mode()
case Warmup4Mode:
strip.updateWarmup4Mode()
case OwnedMode:
strip.updateOwnedMode()
case NotOwnedMode:
strip.updateNotOwnedMode()
case ForceMode:
strip.updateForceMode()
case BoostMode:
strip.updateBoostMode()
case RandomMode:
strip.updateRandomMode()
case FadeRedBlueMode:
strip.updateFadeRedBlueMode()
case FadeSingleMode:
strip.updateFadeSingleMode()
case GradientMode:
strip.updateGradientMode()
case BlinkMode:
strip.updateBlinkMode()
default:
strip.updateOffMode()
}
strip.counter++
}
// Returns true if the pixel data has changed or it has been too long since the last packet was sent.
func (strip *strip) shouldSendPacket() bool {
for i := 0; i < numPixels; i++ {
if strip.pixels[i] != strip.oldPixels[i] {
return true
}
}
return time.Since(strip.lastPacketTime).Seconds() > packetTimeoutSec
}
// Writes the pixel RGB values into the given packet in preparation for sending.
func (strip *strip) populatePacketPixels(pixelData []byte) {
for i, pixel := range strip.pixels {
pixelData[3*i] = pixel[0]
pixelData[3*i+1] = pixel[1]
pixelData[3*i+2] = pixel[2]
}
// Keep a record of the pixel values in order to detect future changes.
strip.oldPixels = strip.pixels
strip.lastPacketTime = time.Now()
}
// Returns the primary color (red or blue) of this strip.
func (strip *strip) getColor() color {
if strip.isRed {
return red
}
return blue
}
// Returns the opposite primary color (red or blue) to this strip.
func (strip *strip) getOppositeColor() color {
if strip.isRed {
return blue
}
return red
}
// Returns a color partway between purple and the primary color (red or blue) of this strip.
func (strip *strip) getMidColor() color {
if strip.isRed {
return purpleBlue
}
return purpleRed
}
// Returns a dim version of the primary color (red or blue) of this strip.
func (strip *strip) getDimColor() color {
if strip.isRed {
return dimRed
}
return dimBlue
}
// Returns a dim version of the opposite primary color (red or blue) of this strip.
func (strip *strip) getDimOppositeColor() color {
if strip.isRed {
return dimBlue
}
return dimRed
}
// Returns the starting offset for the gradient mode for this strip.
func (strip *strip) getGradientStartOffset() int {
if strip.isRed {
return numPixels / 3
}
return 2 * numPixels / 3
}
func (strip *strip) updateOffMode() {
for i := 0; i < numPixels; i++ {
strip.pixels[i] = colors[black]
}
}
func (strip *strip) updateSingleColorMode(color color) {
for i := 0; i < numPixels; i++ {
strip.pixels[i] = colors[color]
}
}
func (strip *strip) updateChaseMode() {
if strip.counter == int(black)*numPixels { // Ignore colors listed after white.
strip.counter = 0
}
color := color(strip.counter / numPixels)
pixelIndex := strip.counter % numPixels
strip.pixels[pixelIndex] = colors[color]
}
func (strip *strip) updateWarmupMode() {
endCounter := 250
if strip.counter == 0 {
// Show solid white to start.
for i := 0; i < numPixels; i++ {
strip.pixels[i] = colors[white]
}
} else if strip.counter <= endCounter {
// Build to the alliance color from each side.
numLitPixels := numPixels / 2 * strip.counter / endCounter
for i := 0; i < numLitPixels; i++ {
strip.pixels[i] = colors[strip.getColor()]
strip.pixels[numPixels-i-1] = colors[strip.getColor()]
}
} else {
// Prevent the counter from rolling over.
strip.counter = endCounter
}
}
func (strip *strip) updateWarmup2Mode() {
startCounter := 100
endCounter := 250
if strip.counter < startCounter {
// Show solid purple to start.
for i := 0; i < numPixels; i++ {
strip.pixels[i] = colors[purple]
}
} else if strip.counter <= endCounter {
for i := 0; i < numPixels; i++ {
strip.pixels[i] = getFadeColor(purple, strip.getColor(), strip.counter-startCounter,
endCounter-startCounter)
}
} else {
// Prevent the counter from rolling over.
strip.counter = endCounter
}
}
func (strip *strip) updateWarmup3Mode() {
startCounter := 50
middleCounter := 225
endCounter := 250
if strip.counter < startCounter {
// Show solid purple to start.
for i := 0; i < numPixels; i++ {
strip.pixels[i] = colors[purple]
}
} else if strip.counter < middleCounter {
for i := 0; i < numPixels; i++ {
strip.pixels[i] = getFadeColor(purple, strip.getMidColor(), strip.counter-startCounter,
middleCounter-startCounter)
}
} else if strip.counter <= endCounter {
for i := 0; i < numPixels; i++ {
strip.pixels[i] = getFadeColor(strip.getMidColor(), strip.getColor(), strip.counter-middleCounter,
endCounter-middleCounter)
}
} else {
// Maintain the current value and prevent the counter from rolling over.
strip.counter = endCounter
}
}
func (strip *strip) updateWarmup4Mode() {
middleCounter := 100
for i := 0; i < numPixels; i++ {
strip.pixels[numPixels-i-1] = getGradientColor(i+strip.counter+strip.getGradientStartOffset(), numPixels/2)
}
if strip.counter >= middleCounter {
for i := 0; i < numPixels; i++ {
if i < strip.counter-middleCounter {
strip.pixels[i] = colors[strip.getColor()]
}
}
}
}
func (strip *strip) updateOwnedMode() {
speedDivisor := 30
pixelSpacing := 4
if strip.counter%speedDivisor != 0 {
return
}
for i := 0; i < numPixels; i++ {
switch (i + strip.counter/speedDivisor) % pixelSpacing {
case 0:
fallthrough
case 1:
strip.pixels[len(strip.pixels)-i-1] = colors[strip.getColor()]
default:
strip.pixels[len(strip.pixels)-i-1] = colors[black]
}
}
}
func (strip *strip) updateNotOwnedMode() {
for i := 0; i < numPixels; i++ {
strip.pixels[i] = colors[strip.getDimColor()]
}
}
func (strip *strip) updateForceMode() {
speedDivisor := 30
pixelSpacing := 7
if strip.counter%speedDivisor != 0 {
return
}
for i := 0; i < numPixels; i++ {
switch (i + strip.counter/speedDivisor) % pixelSpacing {
case 2:
fallthrough
case 4:
strip.pixels[i] = colors[strip.getColor()]
case 3:
strip.pixels[i] = colors[strip.getDimOppositeColor()]
default:
strip.pixels[i] = colors[black]
}
}
}
func (strip *strip) updateBoostMode() {
speedDivisor := 4
pixelSpacing := 4
if strip.counter%speedDivisor != 0 {
return
}
for i := 0; i < numPixels; i++ {
if i%pixelSpacing == strip.counter/speedDivisor%pixelSpacing {
strip.pixels[i] = colors[strip.getColor()]
} else {
strip.pixels[i] = colors[black]
}
}
}
func (strip *strip) updateRandomMode() {
if strip.counter%10 != 0 {
return
}
for i := 0; i < numPixels; i++ {
strip.pixels[i] = colors[color(rand.Intn(int(black)))] // Ignore colors listed after white.
}
}
func (strip *strip) updateFadeRedBlueMode() {
fadeCycles := 40
holdCycles := 10
if strip.counter == 4*holdCycles+4*fadeCycles {
strip.counter = 0
}
for i := 0; i < numPixels; i++ {
if strip.counter < holdCycles {
strip.pixels[i] = colors[black]
} else if strip.counter < holdCycles+fadeCycles {
strip.pixels[i] = getFadeColor(black, red, strip.counter-holdCycles, fadeCycles)
} else if strip.counter < 2*holdCycles+fadeCycles {
strip.pixels[i] = colors[red]
} else if strip.counter < 2*holdCycles+2*fadeCycles {
strip.pixels[i] = getFadeColor(red, black, strip.counter-2*holdCycles-fadeCycles, fadeCycles)
} else if strip.counter < 3*holdCycles+2*fadeCycles {
strip.pixels[i] = colors[black]
} else if strip.counter < 3*holdCycles+3*fadeCycles {
strip.pixels[i] = getFadeColor(black, blue, strip.counter-3*holdCycles-2*fadeCycles, fadeCycles)
} else if strip.counter < 4*holdCycles+3*fadeCycles {
strip.pixels[i] = colors[blue]
} else if strip.counter < 4*holdCycles+4*fadeCycles {
strip.pixels[i] = getFadeColor(blue, black, strip.counter-4*holdCycles-3*fadeCycles, fadeCycles)
}
}
}
func (strip *strip) updateFadeSingleMode() {
offCycles := 50
fadeCycles := 100
if strip.counter == offCycles+2*fadeCycles {
strip.counter = 0
}
for i := 0; i < numPixels; i++ {
if strip.counter < offCycles {
strip.pixels[i] = colors[black]
} else if strip.counter < offCycles+fadeCycles {
strip.pixels[i] = getFadeColor(black, strip.getColor(), strip.counter-offCycles, fadeCycles)
} else if strip.counter < offCycles+2*fadeCycles {
strip.pixels[i] = getFadeColor(strip.getColor(), black, strip.counter-offCycles-fadeCycles, fadeCycles)
}
}
}
func (strip *strip) updateGradientMode() {
for i := 0; i < numPixels; i++ {
strip.pixels[numPixels-i-1] = getGradientColor(i+strip.counter, 75)
}
}
func (strip *strip) updateBlinkMode() {
divisor := 10
for i := 0; i < numPixels; i++ {
if strip.counter%divisor < divisor/2 {
strip.pixels[i] = colors[white]
} else {
strip.pixels[i] = colors[black]
}
}
}
// Interpolates between the two colors based on the given fraction.
func getFadeColor(fromColor, toColor color, numerator, denominator int) [3]byte {
from := colors[fromColor]
to := colors[toColor]
var fadeColor [3]byte
for i := 0; i < 3; i++ {
fadeColor[i] = byte(int(from[i]) + numerator*(int(to[i])-int(from[i]))/denominator)
}
return fadeColor
}
// Calculates the value of a single pixel in a gradient.
func getGradientColor(offset, numPixels int) [3]byte {
offset %= numPixels
if 3*offset < numPixels {
return getFadeColor(red, green, 3*offset, numPixels)
} else if 3*offset < 2*numPixels {
return getFadeColor(green, blue, 3*offset-numPixels, numPixels)
} else {
return getFadeColor(blue, red, 3*offset-2*numPixels, numPixels)
}
}