This commit is contained in:
Kai Vogelgesang 2021-11-13 13:07:59 +01:00
parent 035405bfcc
commit 13baa93aff
Signed by: kai
GPG Key ID: 0A95D3B6E62C0879
8 changed files with 173 additions and 4 deletions

View File

@ -6,6 +6,8 @@ import { TestPattern } from '../patterns/test';
import rust, { BeatTrackerHandle, MovingHeadState, OutputHandle, TrackerConfig } from 'rust_native_module'; import rust, { BeatTrackerHandle, MovingHeadState, OutputHandle, TrackerConfig } from 'rust_native_module';
import { ChaserPattern } from '../patterns/chaser'; import { ChaserPattern } from '../patterns/chaser';
import { HSLRandomMovementPattern } from '../patterns/hslRandom'; import { HSLRandomMovementPattern } from '../patterns/hslRandom';
import { HSLRandomMovementPairsPattern } from '../patterns/hslRandom2';
import { SnoopDoggPattern } from '../patterns/snoop';
export type AppState = { export type AppState = {
patterns: { [key: string]: PatternOutput }, patterns: { [key: string]: PatternOutput },
@ -63,6 +65,8 @@ class Backend {
this.patterns.set("test", new TestPattern()); this.patterns.set("test", new TestPattern());
this.patterns.set("chaser", new ChaserPattern()); this.patterns.set("chaser", new ChaserPattern());
this.patterns.set("hslRandom", new HSLRandomMovementPattern()); this.patterns.set("hslRandom", new HSLRandomMovementPattern());
this.patterns.set("snoop", new SnoopDoggPattern());
this.patterns.set("hslRandomPairs", new HSLRandomMovementPairsPattern());
this.state = { this.state = {
patterns: {}, patterns: {},

View File

@ -0,0 +1,105 @@
import { panLeft, panRight, startAddresses, tiltDown, tiltUp } from './stage'
import { Pattern, PatternOutput, RenderUpdate } from './proto';
import { MovingHeadState } from 'rust_native_module';
import { Tuple4 } from './types';
import { hsl2rgb, clamp, rescale } from './util';
const HSL_CYCLE_LENGTH: number = 7; // seconds
const MOVEMENT_CYCLE_LENGTH: number = 0.5; // seconds?
const THRESHOLD: number = 1.5;
const panBounds: Tuple4<[number, number]> = [
[panLeft, panRight],
[panLeft, panRight],
[panLeft, panRight],
[panLeft, panRight],
];
const tiltBound: [number, number] = [tiltDown, tiltUp];
export class HSLRandomMovementPairsPattern implements Pattern {
brightness: number;
targets: [[number, number], [number, number]];
lastUpdate: number;
constructor() {
this.brightness = 0;
this.targets = [
[panLeft, tiltUp],
[panRight, tiltUp],
]
this.lastUpdate = 0;
}
render(update: RenderUpdate): PatternOutput {
// color
const t = update.absolute;
const h = 360 * ((t % HSL_CYCLE_LENGTH) / HSL_CYCLE_LENGTH);
const s = 1;
const v = 0.5;
const [r, g, b] = hsl2rgb(h, s, v);
const rgbw1: Tuple4<number> = [
Math.round(r * 255),
Math.round(g * 255),
Math.round(b * 255),
0
];
const [r2, g2, b2] = hsl2rgb((h + 90) % 360, s, v);
const rgbw2: Tuple4<number> = [
Math.round(r2 * 255),
Math.round(g2 * 255),
Math.round(b2 * 255),
0
];
// brightness
this.brightness = update.bassVolume > THRESHOLD
? 1
: 0.75 * this.brightness;
// movement
if (t - this.lastUpdate > MOVEMENT_CYCLE_LENGTH) {
this.lastUpdate = t;
for (let index in [0, 1]) {
const bound = panBounds[index];
const pan = rescale(Math.random(), { to: bound });
const tilt = clamp(Math.acos(Math.random()), tiltBound);
this.targets[index] = [pan, tilt];
}
}
return startAddresses.map((startAddress, index) => {
const [pan, tilt] = this.targets[index % 2];
const rgbw = [rgbw1, rgbw2][index % 2];
let state: MovingHeadState = {
startAddress: startAddress,
pan: pan,
tilt: tilt,
brightness: {
type: 'dimmer',
value: 0.2 + 0.8 * this.brightness,
},
rgbw: rgbw,
speed: 1,
reset: false
}
return state;
}) as PatternOutput;
}
}

View File

@ -0,0 +1,55 @@
import { MovingHeadState } from "rust_native_module";
import { Pattern, PatternOutput, RenderUpdate } from "./proto";
import { down, panLeft, panRight, startAddresses, tiltUp } from "./stage";
import { Tuple4 } from "./types";
const SNOOPY_COLOR: Tuple4<number> = [
0, 255, 0, 0
]
const LEFT_RIGHT_TIME: number = 5 // seconds;
export class SnoopDoggPattern implements Pattern {
lastUpdate: number;
left: boolean;
constructor() {
this.lastUpdate = 0;
this.left = true;
}
render(update: RenderUpdate): PatternOutput {
const t = update.absolute;
if (t - this.lastUpdate > LEFT_RIGHT_TIME) {
this.left = !this.left;
this.lastUpdate = t;
}
const tiltTarget = tiltUp + 0.25 * Math.PI * down;
const target = this.left
? [panLeft, tiltTarget]
: [panRight, tiltTarget];
const [pan, tilt] = target;
return (startAddresses.map((startAddress) => {
const state: MovingHeadState = {
startAddress: startAddress,
pan: pan,
tilt: tilt,
brightness: {
type: "dimmer",
value: 1,
},
rgbw: SNOOPY_COLOR,
speed: 0.1,
reset: false
}
return state;
})) as PatternOutput
}
}

View File

@ -4,7 +4,12 @@ export const startAddresses: Tuple4<number> = [1, 15, 29, 43]
export const panLeft = 0; export const panLeft = 0;
export const panRight = Math.PI; export const panRight = Math.PI;
export const panForward = (panLeft + panRight) / 2; export const panForward = 0.5 * Math.PI;
export const left = -1;
export const right = 1;
export const up = 1;
export const down = -1;
export const tiltUp = 0.5 * Math.PI; export const tiltUp = 0.5 * Math.PI;
export const tiltDown = 0; export const tiltDown = 0;

View File

@ -13,7 +13,7 @@ export class TestPattern implements Pattern {
] ]
render(update: RenderUpdate): PatternOutput { render(update: RenderUpdate): PatternOutput {
let t = update.absolute % this.rgbw.length; let t = (1.5 * update.absolute) % this.rgbw.length;
let second = Math.floor(t); let second = Math.floor(t);
let brightness = 1 - (t % 1); let brightness = 1 - (t % 1);

View File

@ -45,7 +45,7 @@ const Frontend: React.FC<{ state: AppState }> = ({ state }) => {
return <div className="container"> return <div className="container">
<ConfigControls updateDelay={state.trackerConfig.zeroCrossingBeatDelay} /> <ConfigControls updateDelay={state.trackerConfig.zeroCrossingBeatDelay} />
<div> <div>
<BongoCat onClick={tap} beatProgress={state.beatProgress} /> <BongoCat onClick={tap} beatProgress={state.beatProgress} high={state.selectedPattern === "snoop"}/>
</div> </div>
<div style={{ width: "80%", marginTop: 80 }} className="element-row"> <div style={{ width: "80%", marginTop: 80 }} className="element-row">
{ {

View File

@ -38,7 +38,7 @@ const Cone: React.FC<{ color: string, translate: [number, number] , pan : number
// TODO: adjust if necessary // TODO: adjust if necessary
const degreesPan = pan / (2* Math.PI) * 360 - 45; const degreesPan = pan / (2* Math.PI) * 360 - 45;
console.log(degreesTilt); // console.log(degreesTilt);
return <g className="cone" style={{transform: `rotate(${degreesTilt}deg) rotateX(${degreesPan}deg)`}} > return <g className="cone" style={{transform: `rotate(${degreesTilt}deg) rotateX(${degreesPan}deg)`}} >
<path stroke="black" fill="gray" d="M 15 0 L 45 0 L 60 30 L 0 30 L 15 0" /> <path stroke="black" fill="gray" d="M 15 0 L 45 0 L 60 30 L 0 30 L 15 0" />
<path fill={color} d="M 15 30 L 0 150 L 60 150 L 45 30 " /> <path fill={color} d="M 15 30 L 0 150 L 60 150 L 45 30 " />

Binary file not shown.

After

Width:  |  Height:  |  Size: 296 KiB