diff --git a/boilerbloat/src/main/backend.ts b/boilerbloat/src/main/backend.ts index d9825f4..e5b1421 100644 --- a/boilerbloat/src/main/backend.ts +++ b/boilerbloat/src/main/backend.ts @@ -2,12 +2,13 @@ import { ipcMain } from 'electron'; import { blackout } from '../patterns/blackout'; import { Pattern, PatternOutput, RenderUpdate } from '../patterns/proto'; -import { TestPattern } from '../patterns/test'; +// import { TestPattern } from '../patterns/test'; import rust, { BeatTrackerHandle, MovingHeadState, OutputHandle, TrackerConfig } from 'rust_native_module'; import { ChaserPattern } from '../patterns/chaser'; import { HSLRandomMovementPattern } from '../patterns/hslRandom'; import { HSLRandomMovementPairsPattern } from '../patterns/hslRandom2'; import { SnoopDoggPattern } from '../patterns/snoop'; +import { StroboInsGesichtPattern } from '../patterns/stroboInsGesichtLol'; export type AppState = { patterns: { [key: string]: PatternOutput }, @@ -62,11 +63,12 @@ class Backend { // patterns this.patterns = new Map(); - this.patterns.set("test", new TestPattern()); - this.patterns.set("chaser", new ChaserPattern()); + // this.patterns.set("test", new TestPattern()); this.patterns.set("hslRandom", new HSLRandomMovementPattern()); - this.patterns.set("snoop", new SnoopDoggPattern()); this.patterns.set("hslRandomPairs", new HSLRandomMovementPairsPattern()); + this.patterns.set("chaser", new ChaserPattern()); + this.patterns.set("snoop", new SnoopDoggPattern()); + this.patterns.set("stroboInsGesictLOL", new StroboInsGesichtPattern()); this.state = { patterns: {}, @@ -142,8 +144,19 @@ class Backend { let patternOutput = pattern.render(update); this.state.patterns[patternId] = patternOutput; - if (patternId === this.state.selectedPattern && patternOutput) { - output = patternOutput; + if (patternId === this.state.selectedPattern) { + if (patternOutput) { + output = patternOutput; + } else { + + let choices = [ + 'hslRandom', + 'hslRandomPairs', + ]; + + this.state.selectedPattern = choices[Math.floor(Math.random() * choices.length)] + + } } } diff --git a/boilerbloat/src/patterns/chaser.ts b/boilerbloat/src/patterns/chaser.ts index 749e867..dd86430 100644 --- a/boilerbloat/src/patterns/chaser.ts +++ b/boilerbloat/src/patterns/chaser.ts @@ -1,42 +1,77 @@ import { MovingHeadState } from 'rust_native_module'; import { Pattern, PatternOutput, RenderUpdate } from './proto'; +import { TARGETS } from './stroboInsGesichtLol'; +import { startAddresses } from './stage'; +import { hsl2rgb, rescale } from './util'; import { Tuple4 } from './types'; -const template: MovingHeadState = { - startAddress: 0, - pan: 0, - tilt: 0, - brightness: { - type: 'dimmer', - value: 0.2, - }, - rgbw: [255, 0, 0, 0], - speed: 1, - reset: false, -} - export class ChaserPattern implements Pattern { - render(update: RenderUpdate): PatternOutput { + lastUpdate: number; + rgbw: Tuple4; - if (update.beatRelative === null) { - return null; - } + constructor() { + this.lastUpdate = -8; + this.rgbw = [0, 0, 0, 255]; + } - let t = update.beatRelative; + render(update: RenderUpdate): PatternOutput { - let head_number = Math.floor(t % 4); - - let result: Tuple4 = [{ ...template }, { ...template }, { ...template }, { ...template }]; - - [1, 15, 29, 43].forEach((startAddress, i) => { - result[i].startAddress = startAddress; - - if (i === head_number) { - result[i].brightness = { type: 'dimmer', value: 1 }; - } - }); - - return result; + if (update.beatRelative === null) { + this.lastUpdate = -8; + return null; } + + let t = update.beatRelative; + + if (t - this.lastUpdate > 4) { + this.lastUpdate = Math.floor(t); + + if (Math.random() > 0.2) { + + const h = rescale(Math.random(), { to: [0, 360] }); + const s = 1; + const l = 0.5; + + const [r, g, b] = hsl2rgb(h, s, l); + this.rgbw = [ + Math.round(r * 255), + Math.round(g * 255), + Math.round(b * 255), + 0 + ] + + } else { + this.rgbw = [ + 0, 0, 0, 255 + ]; + } + } + + let head_number = Math.floor(t % 4); + + return startAddresses.map((address, index) => { + const [pan, tilt] = TARGETS[index]; + + const state: MovingHeadState = { + startAddress: address, + pan: pan, + tilt: tilt, + brightness: { + type: 'dimmer', + value: 0.1, + }, + rgbw: this.rgbw, + speed: 1, + reset: false + } + + if (index === head_number) { + state.brightness = { type: 'dimmer', value: 0.6 }; + } + + return state; + }) as PatternOutput; + + } } diff --git a/boilerbloat/src/patterns/stroboInsGesichtLol.ts b/boilerbloat/src/patterns/stroboInsGesichtLol.ts new file mode 100644 index 0000000..4ff9a12 --- /dev/null +++ b/boilerbloat/src/patterns/stroboInsGesichtLol.ts @@ -0,0 +1,35 @@ +import { MovingHeadState } from "rust_native_module"; +import { Pattern, PatternOutput, RenderUpdate } from "./proto"; +import { left, right, panForward, startAddresses, tiltUp, down } from "./stage"; +import { Tuple4 } from "./types"; + +export const TARGETS: Tuple4<[number, number]> = [ + [panForward + Math.atan(1) * left, tiltUp + Math.atan(1 / 3) * down], + [panForward + Math.atan(1 / 3) * left, tiltUp + Math.atan(1 / 3) * down], + [panForward + Math.atan(1 / 3) * right, tiltUp + Math.atan(1 / 3) * down], + [panForward + Math.atan(1) * right, tiltUp + Math.atan(1 / 3) * down], +] + +export class StroboInsGesichtPattern implements Pattern { + render(_update: RenderUpdate): PatternOutput { + return startAddresses.map(((address, index) => { + + const [pan, tilt] = TARGETS[index]; + + const state: MovingHeadState = { + startAddress: address, + pan: pan, + tilt: tilt, + brightness: { + type: "strobe", + value: 1, + }, + rgbw: [0, 0, 0, 255], + speed: 1, + reset: false + } + + return state; + })) as PatternOutput + } +} diff --git a/boilerbloat/src/renderer/App.tsx b/boilerbloat/src/renderer/App.tsx index 39c50db..4ea74a3 100644 --- a/boilerbloat/src/renderer/App.tsx +++ b/boilerbloat/src/renderer/App.tsx @@ -1,14 +1,14 @@ -import { MemoryRouter as Router, Switch, Route } from 'react-router-dom'; import { IpcRenderer } from 'electron/renderer'; - -import './App.css'; import { useEffect, useState } from 'react'; - +import { MemoryRouter as Router, Route, Switch } from 'react-router-dom'; import { AppState } from '../main/backend'; -import PatternPreview from './PatternPreview'; -import GraphVisualization from './Graph'; -import ConfigControls from './ConfigControls'; +import './App.css'; import { BongoCat } from './BongoCat'; +import ConfigControls from './ConfigControls'; +import GraphVisualization from './Graph'; +import PatternPreview from './PatternPreview'; + + const ipcRenderer = (window as any).electron.ipcRenderer as IpcRenderer; @@ -50,7 +50,7 @@ const Frontend: React.FC<{ state: AppState }> = ({ state }) => {
{ Object.entries(state.patterns).map(([patternId, output]) => ( - + )) }
diff --git a/boilerbloat/src/renderer/PatternPreview.tsx b/boilerbloat/src/renderer/PatternPreview.tsx index 6a5a403..630d077 100644 --- a/boilerbloat/src/renderer/PatternPreview.tsx +++ b/boilerbloat/src/renderer/PatternPreview.tsx @@ -1,28 +1,59 @@ import { IpcRenderer } from "electron/renderer"; +import { useState } from "react"; import { PatternOutput } from "../patterns/proto"; - import './patterns.css'; +import _snoop from '../res/snoop.png'; +import _strobo from '../res/strobo.jpg'; + + const ipcRenderer = (window as any).electron.ipcRenderer as IpcRenderer; -const PatternPreview: React.FC<{ patternId: string, output: PatternOutput , selected: boolean}> = ({ patternId, output, selected}) => { +const PatternPreview: React.FC<{ patternId: string, output: PatternOutput, selectedPattern: string }> = ({ patternId, output, selectedPattern }) => { + const [previousPattern, setPreviousPattern] = useState(patternId); + const selected = selectedPattern === patternId; + const selectedClass = selected ? " selected" : ""; const styleClass = "pattern-button" + selectedClass; + + const styleObj: React.CSSProperties = {}; + + if (patternId === "snoop") { + styleObj.background = "none"; + } + return
- - ipcRenderer.send("pattern-select", patternId) - } width={260 + 2*70} height="150"> + { + console.log("umu on", patternId); + if (patternId === "stroboInsGesictLOL") { + // restore after strobe + ipcRenderer.send("pattern-select", previousPattern); + } + }} + onMouseDown={() => { + console.log("omd on", patternId); + console.log("storing", selectedPattern); + // store before strobe + setPreviousPattern(selectedPattern); + ipcRenderer.send("pattern-select", patternId) + }} + //onClick={() => + // ipcRenderer.send("pattern-select", patternId) + //} + width={260 + 2 * 70} height="150"> {output?.map((headState, index) => { const [r, g, b, w] = headState.rgbw; const brightness = "value" in headState.brightness ? headState.brightness.value : 0; - return + return } )}
} -const ColorCone: React.FC<{ pan: number, tilt: number, color: string, translate: [number, number], white: number}> = ({ color, translate: [x, y], white, pan, tilt }) => { +const ColorCone: React.FC<{ pan: number, tilt: number, color: string, translate: [number, number], white: number }> = ({ color, translate: [x, y], white, pan, tilt }) => { return {white === 0 ? null : @@ -32,14 +63,14 @@ const ColorCone: React.FC<{ pan: number, tilt: number, color: string, translate: }; -const Cone: React.FC<{ color: string, translate: [number, number] , pan : number, tilt: number}> = ({ color , tilt, pan}) => { +const Cone: React.FC<{ color: string, translate: [number, number], pan: number, tilt: number }> = ({ color, tilt, pan }) => { // tilt is correct with -45 degree offset, but if lights are pointing down, they will be displayed to point down right - const degreesTilt = tilt / (2* Math.PI) * 360 - 45; + const degreesTilt = tilt / (2 * Math.PI) * 360 - 45; // TODO: adjust if necessary - const degreesPan = pan / (2* Math.PI) * 360 - 45; + const degreesPan = pan / (2 * Math.PI) * 360 - 45; // console.log(degreesTilt); - return + return ; diff --git a/boilerbloat/src/res/strobo.jpg b/boilerbloat/src/res/strobo.jpg new file mode 100644 index 0000000..953a2ca Binary files /dev/null and b/boilerbloat/src/res/strobo.jpg differ