134 lines
3.2 KiB
JavaScript
134 lines
3.2 KiB
JavaScript
// Frame utilities for LEDLab presets
|
|
|
|
const SERPENTINE_WIRING = true;
|
|
|
|
function clamp(value, min, max) {
|
|
return Math.max(min, Math.min(max, value));
|
|
}
|
|
|
|
function hexToRgb(hex) {
|
|
const normalizedHex = hex.trim().toLowerCase();
|
|
const value = normalizedHex.startsWith('#') ? normalizedHex.slice(1) : normalizedHex;
|
|
return {
|
|
r: parseInt(value.slice(0, 2), 16),
|
|
g: parseInt(value.slice(2, 4), 16),
|
|
b: parseInt(value.slice(4, 6), 16),
|
|
};
|
|
}
|
|
|
|
function rgbToHex(rgb) {
|
|
return toHex(rgb.r) + toHex(rgb.g) + toHex(rgb.b);
|
|
}
|
|
|
|
function toHex(value) {
|
|
const boundedValue = clamp(Math.round(value), 0, 255);
|
|
const hex = boundedValue.toString(16);
|
|
return hex.length === 1 ? '0' + hex : hex;
|
|
}
|
|
|
|
function lerpRgb(lhs, rhs, t) {
|
|
return {
|
|
r: Math.round(lhs.r + (rhs.r - lhs.r) * t),
|
|
g: Math.round(lhs.g + (rhs.g - lhs.g) * t),
|
|
b: Math.round(lhs.b + (rhs.b - lhs.b) * t),
|
|
};
|
|
}
|
|
|
|
function samplePalette(paletteStops, value) {
|
|
const clampedValue = clamp(value, 0, 1);
|
|
|
|
for (let index = 0; index < paletteStops.length - 1; ++index) {
|
|
const left = paletteStops[index];
|
|
const right = paletteStops[index + 1];
|
|
if (clampedValue <= right.stop) {
|
|
const span = right.stop - left.stop || 1;
|
|
const t = clamp((clampedValue - left.stop) / span, 0, 1);
|
|
const interpolatedColor = lerpRgb(left.color, right.color, t);
|
|
return rgbToHex(interpolatedColor);
|
|
}
|
|
}
|
|
|
|
return rgbToHex(paletteStops[paletteStops.length - 1].color);
|
|
}
|
|
|
|
function toIndex(col, row, width, serpentine = SERPENTINE_WIRING) {
|
|
if (!serpentine || row % 2 === 0) {
|
|
return row * width + col;
|
|
}
|
|
return row * width + (width - 1 - col);
|
|
}
|
|
|
|
function createFrame(width, height, fill = '000000') {
|
|
return new Array(width * height).fill(fill);
|
|
}
|
|
|
|
function frameToPayload(frame) {
|
|
return 'RAW:' + frame.join('');
|
|
}
|
|
|
|
function fadeFrame(frame, factor) {
|
|
for (let index = 0; index < frame.length; ++index) {
|
|
const hex = frame[index];
|
|
const r = parseInt(hex.slice(0, 2), 16) * factor;
|
|
const g = parseInt(hex.slice(2, 4), 16) * factor;
|
|
const b = parseInt(hex.slice(4, 6), 16) * factor;
|
|
frame[index] = toHex(r) + toHex(g) + toHex(b);
|
|
}
|
|
}
|
|
|
|
function addRgbToFrame(frame, index, rgb) {
|
|
if (index < 0 || index >= frame.length) {
|
|
return;
|
|
}
|
|
|
|
const current = hexToRgb(frame[index]);
|
|
const updated = {
|
|
r: clamp(current.r + rgb.r, 0, 255),
|
|
g: clamp(current.g + rgb.g, 0, 255),
|
|
b: clamp(current.b + rgb.b, 0, 255),
|
|
};
|
|
frame[index] = rgbToHex(updated);
|
|
}
|
|
|
|
function addHexColor(frame, index, hexColor, intensity = 1) {
|
|
if (intensity <= 0) {
|
|
return;
|
|
}
|
|
|
|
const base = hexToRgb(hexColor);
|
|
addRgbToFrame(frame, index, {
|
|
r: base.r * intensity,
|
|
g: base.g * intensity,
|
|
b: base.b * intensity,
|
|
});
|
|
}
|
|
|
|
// Color wheel function for rainbow effects
|
|
function wheel(pos) {
|
|
pos = 255 - pos;
|
|
if (pos < 85) {
|
|
return [255 - pos * 3, 0, pos * 3];
|
|
}
|
|
if (pos < 170) {
|
|
pos -= 85;
|
|
return [0, pos * 3, 255 - pos * 3];
|
|
}
|
|
pos -= 170;
|
|
return [pos * 3, 255 - pos * 3, 0];
|
|
}
|
|
|
|
module.exports = {
|
|
clamp,
|
|
hexToRgb,
|
|
rgbToHex,
|
|
lerpRgb,
|
|
samplePalette,
|
|
toIndex,
|
|
createFrame,
|
|
frameToPayload,
|
|
fadeFrame,
|
|
addRgbToFrame,
|
|
addHexColor,
|
|
wheel,
|
|
};
|