feat: ledlab
This commit is contained in:
152
presets/circuit-pulse-preset.js
Normal file
152
presets/circuit-pulse-preset.js
Normal file
@@ -0,0 +1,152 @@
|
||||
// Circuit Pulse preset for LEDLab
|
||||
|
||||
const BasePreset = require('./base-preset');
|
||||
const { createFrame, fadeFrame, frameToPayload, hexToRgb, samplePalette, toIndex, addHexColor } = require('./frame-utils');
|
||||
|
||||
class CircuitPulsePreset extends BasePreset {
|
||||
constructor(width = 16, height = 16) {
|
||||
super(width, height);
|
||||
this.paths = [];
|
||||
this.pulses = [];
|
||||
this.paletteStops = [
|
||||
{ stop: 0.0, color: hexToRgb('020209') },
|
||||
{ stop: 0.3, color: hexToRgb('023047') },
|
||||
{ stop: 0.6, color: hexToRgb('115173') },
|
||||
{ stop: 0.8, color: hexToRgb('1ca78f') },
|
||||
{ stop: 1.0, color: hexToRgb('94fdf3') },
|
||||
];
|
||||
this.accentColors = ['14f5ff', 'a7ff4d', 'ffcc3f'];
|
||||
this.defaultParameters = {
|
||||
pathFade: 0.85,
|
||||
pulseLength: 6,
|
||||
pulseSpeed: 5.0,
|
||||
pulseCount: 3,
|
||||
};
|
||||
}
|
||||
|
||||
init() {
|
||||
super.init();
|
||||
this.paths = this.createPaths(this.width, this.height);
|
||||
this.pulses = this.createPulses(this.paths.length);
|
||||
}
|
||||
|
||||
createPaths(matrixWidth, matrixHeight) {
|
||||
const horizontalStep = Math.max(2, Math.floor(matrixHeight / 4));
|
||||
const verticalStep = Math.max(2, Math.floor(matrixWidth / 4));
|
||||
const generatedPaths = [];
|
||||
|
||||
// Horizontal paths
|
||||
for (let y = 1; y < matrixHeight; y += horizontalStep) {
|
||||
const path = [];
|
||||
for (let x = 0; x < matrixWidth; ++x) {
|
||||
path.push({ x, y });
|
||||
}
|
||||
generatedPaths.push(path);
|
||||
}
|
||||
|
||||
// Vertical paths
|
||||
for (let x = 2; x < matrixWidth; x += verticalStep) {
|
||||
const path = [];
|
||||
for (let y = 0; y < matrixHeight; ++y) {
|
||||
path.push({ x, y });
|
||||
}
|
||||
generatedPaths.push(path);
|
||||
}
|
||||
|
||||
return generatedPaths;
|
||||
}
|
||||
|
||||
createPulses(count) {
|
||||
const pulseList = [];
|
||||
for (let index = 0; index < count; ++index) {
|
||||
pulseList.push(this.spawnPulse(index));
|
||||
}
|
||||
return pulseList;
|
||||
}
|
||||
|
||||
spawnPulse(pathIndex) {
|
||||
const color = this.accentColors[pathIndex % this.accentColors.length];
|
||||
return {
|
||||
pathIndex,
|
||||
position: 0,
|
||||
speed: 3 + Math.random() * 2,
|
||||
color,
|
||||
};
|
||||
}
|
||||
|
||||
updatePulse(pulse, deltaSeconds) {
|
||||
pulse.position += pulse.speed * deltaSeconds;
|
||||
const path = this.paths[pulse.pathIndex];
|
||||
|
||||
if (!path || path.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (pulse.position >= path.length + this.getParameter('pulseLength')) {
|
||||
Object.assign(pulse, this.spawnPulse(pulse.pathIndex));
|
||||
pulse.position = 0;
|
||||
}
|
||||
}
|
||||
|
||||
renderPulse(pulse) {
|
||||
const path = this.paths[pulse.pathIndex];
|
||||
if (!path) {
|
||||
return;
|
||||
}
|
||||
|
||||
const pulseLength = this.getParameter('pulseLength');
|
||||
|
||||
for (let offset = 0; offset < pulseLength; ++offset) {
|
||||
const index = Math.floor(pulse.position) - offset;
|
||||
if (index < 0 || index >= path.length) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const { x, y } = path[index];
|
||||
const intensity = Math.max(0, 1 - offset / pulseLength);
|
||||
const baseColor = samplePalette(this.paletteStops, intensity);
|
||||
this.frame[toIndex(x, y, this.width)] = baseColor;
|
||||
addHexColor(this.frame, toIndex(x, y, this.width), pulse.color, intensity * 1.4);
|
||||
}
|
||||
}
|
||||
|
||||
renderFrame() {
|
||||
this.frame = createFrame(this.width, this.height);
|
||||
const pathFade = this.getParameter('pathFade') || 0.85;
|
||||
const pulseCount = this.getParameter('pulseCount') || 3;
|
||||
|
||||
fadeFrame(this.frame, pathFade);
|
||||
|
||||
// Update pulse count if it changed
|
||||
while (this.pulses.length < pulseCount) {
|
||||
this.pulses.push(this.spawnPulse(this.pulses.length));
|
||||
}
|
||||
while (this.pulses.length > pulseCount) {
|
||||
this.pulses.pop();
|
||||
}
|
||||
|
||||
this.pulses.forEach((pulse) => {
|
||||
this.updatePulse(pulse, 0.016); // Assume 60 FPS
|
||||
this.renderPulse(pulse);
|
||||
});
|
||||
|
||||
return this.frame;
|
||||
}
|
||||
|
||||
getMetadata() {
|
||||
return {
|
||||
name: 'Circuit Pulse',
|
||||
description: 'Animated circuit board with pulsing paths',
|
||||
parameters: {
|
||||
pathFade: { type: 'range', min: 0.7, max: 0.95, step: 0.05, default: 0.85 },
|
||||
pulseLength: { type: 'range', min: 3, max: 12, step: 1, default: 6 },
|
||||
pulseSpeed: { type: 'range', min: 2.0, max: 8.0, step: 0.5, default: 5.0 },
|
||||
pulseCount: { type: 'range', min: 1, max: 6, step: 1, default: 3 },
|
||||
},
|
||||
width: this.width,
|
||||
height: this.height,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = CircuitPulsePreset;
|
||||
Reference in New Issue
Block a user