Add 5 procedural LED effects, gradient presets, auto-crop min aspect ratio, static source polling optimization
New features: - Procedural effect source type with fire, meteor, plasma, noise, and aurora algorithms using palette LUT system and 1D value noise generator - 12 predefined gradient presets (rainbow, sunset, ocean, forest, fire, lava, aurora, ice, warm, cool, neon, pastel) selectable from a dropdown in the gradient editor - Auto-crop filter: min aspect ratio parameter to prevent false-positive cropping in dark scenes on ultrawide displays Optimization: - Static/gradient sources without animation: stream thread sleeps 0.25s instead of frame_time; processor repolls at frame_time instead of 5ms (~40x fewer iterations) - Inverted isinstance checks in routes to test for PictureColorStripSource only Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -89,7 +89,8 @@ import {
|
||||
// Layer 5: color-strip sources
|
||||
import {
|
||||
showCSSEditor, closeCSSEditorModal, forceCSSEditorClose, saveCSSEditor, deleteColorStrip,
|
||||
onCSSTypeChange, onAnimationTypeChange, colorCycleAddColor, colorCycleRemoveColor,
|
||||
onCSSTypeChange, onEffectTypeChange, onAnimationTypeChange, colorCycleAddColor, colorCycleRemoveColor,
|
||||
applyGradientPreset,
|
||||
} from './features/color-strips.js';
|
||||
|
||||
// Layer 5: calibration
|
||||
@@ -274,9 +275,11 @@ Object.assign(window, {
|
||||
saveCSSEditor,
|
||||
deleteColorStrip,
|
||||
onCSSTypeChange,
|
||||
onEffectTypeChange,
|
||||
onAnimationTypeChange,
|
||||
colorCycleAddColor,
|
||||
colorCycleRemoveColor,
|
||||
applyGradientPreset,
|
||||
|
||||
// calibration
|
||||
showCalibration,
|
||||
|
||||
@@ -31,6 +31,13 @@ class CSSEditorModal extends Modal {
|
||||
animation_speed: document.getElementById('css-editor-animation-speed').value,
|
||||
cycle_speed: document.getElementById('css-editor-cycle-speed').value,
|
||||
cycle_colors: JSON.stringify(_colorCycleColors),
|
||||
effect_type: document.getElementById('css-editor-effect-type').value,
|
||||
effect_speed: document.getElementById('css-editor-effect-speed').value,
|
||||
effect_palette: document.getElementById('css-editor-effect-palette').value,
|
||||
effect_color: document.getElementById('css-editor-effect-color').value,
|
||||
effect_intensity: document.getElementById('css-editor-effect-intensity').value,
|
||||
effect_scale: document.getElementById('css-editor-effect-scale').value,
|
||||
effect_mirror: document.getElementById('css-editor-effect-mirror').checked,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -45,8 +52,11 @@ export function onCSSTypeChange() {
|
||||
document.getElementById('css-editor-static-section').style.display = type === 'static' ? '' : 'none';
|
||||
document.getElementById('css-editor-color-cycle-section').style.display = type === 'color_cycle' ? '' : 'none';
|
||||
document.getElementById('css-editor-gradient-section').style.display = type === 'gradient' ? '' : 'none';
|
||||
document.getElementById('css-editor-effect-section').style.display = type === 'effect' ? '' : 'none';
|
||||
|
||||
// Animation section — shown for static/gradient only (color_cycle is always animating)
|
||||
if (type === 'effect') onEffectTypeChange();
|
||||
|
||||
// Animation section — shown for static/gradient only
|
||||
const animSection = document.getElementById('css-editor-animation-section');
|
||||
const animTypeSelect = document.getElementById('css-editor-animation-type');
|
||||
const noneOpt = `<option value="none">${t('color_strip.animation.type.none')}</option>`;
|
||||
@@ -119,6 +129,31 @@ function _syncAnimationSpeedState() {
|
||||
}
|
||||
}
|
||||
|
||||
/* ── Effect type helpers ──────────────────────────────────────── */
|
||||
|
||||
export function onEffectTypeChange() {
|
||||
const et = document.getElementById('css-editor-effect-type').value;
|
||||
// palette: all except meteor
|
||||
document.getElementById('css-editor-effect-palette-group').style.display = et !== 'meteor' ? '' : 'none';
|
||||
// color picker: meteor only
|
||||
document.getElementById('css-editor-effect-color-group').style.display = et === 'meteor' ? '' : 'none';
|
||||
// intensity: fire, meteor, aurora
|
||||
document.getElementById('css-editor-effect-intensity-group').style.display =
|
||||
['fire', 'meteor', 'aurora'].includes(et) ? '' : 'none';
|
||||
// scale: plasma, noise, aurora
|
||||
document.getElementById('css-editor-effect-scale-group').style.display =
|
||||
['plasma', 'noise', 'aurora'].includes(et) ? '' : 'none';
|
||||
// mirror: meteor only
|
||||
document.getElementById('css-editor-effect-mirror-group').style.display = et === 'meteor' ? '' : 'none';
|
||||
// description
|
||||
const descEl = document.getElementById('css-editor-effect-type-desc');
|
||||
if (descEl) {
|
||||
const desc = t('color_strip.effect.' + et + '.desc') || '';
|
||||
descEl.textContent = desc;
|
||||
descEl.style.display = desc ? '' : 'none';
|
||||
}
|
||||
}
|
||||
|
||||
/* ── Color Cycle helpers ──────────────────────────────────────── */
|
||||
|
||||
const _DEFAULT_CYCLE_COLORS = ['#ff0000', '#ffff00', '#00ff00', '#00ffff', '#0000ff', '#ff00ff'];
|
||||
@@ -197,6 +232,7 @@ export function createColorStripCard(source, pictureSourceMap) {
|
||||
const isStatic = source.source_type === 'static';
|
||||
const isGradient = source.source_type === 'gradient';
|
||||
const isColorCycle = source.source_type === 'color_cycle';
|
||||
const isEffect = source.source_type === 'effect';
|
||||
|
||||
const anim = (isStatic || isGradient) && source.animation && source.animation.enabled ? source.animation : null;
|
||||
const animBadge = anim
|
||||
@@ -246,6 +282,15 @@ export function createColorStripCard(source, pictureSourceMap) {
|
||||
${source.led_count ? `<span class="stream-card-prop" title="${t('color_strip.leds')}">💡 ${source.led_count}</span>` : ''}
|
||||
${animBadge}
|
||||
`;
|
||||
} else if (isEffect) {
|
||||
const effectLabel = t('color_strip.effect.' + (source.effect_type || 'fire')) || source.effect_type || 'fire';
|
||||
const paletteLabel = source.palette ? (t('color_strip.palette.' + source.palette) || source.palette) : '';
|
||||
propsHtml = `
|
||||
<span class="stream-card-prop">⚡ ${escapeHtml(effectLabel)}</span>
|
||||
${paletteLabel ? `<span class="stream-card-prop" title="${t('color_strip.effect.palette')}">🎨 ${escapeHtml(paletteLabel)}</span>` : ''}
|
||||
<span class="stream-card-prop" title="${t('color_strip.effect.speed')}">⏩ ${(source.speed || 1.0).toFixed(1)}×</span>
|
||||
${source.led_count ? `<span class="stream-card-prop" title="${t('color_strip.leds')}">💡 ${source.led_count}</span>` : ''}
|
||||
`;
|
||||
} else {
|
||||
const srcName = (pictureSourceMap && pictureSourceMap[source.picture_source_id])
|
||||
? pictureSourceMap[source.picture_source_id].name
|
||||
@@ -259,8 +304,8 @@ export function createColorStripCard(source, pictureSourceMap) {
|
||||
`;
|
||||
}
|
||||
|
||||
const icon = isStatic ? '🎨' : isColorCycle ? '🔄' : isGradient ? '🌈' : '🎞️';
|
||||
const calibrationBtn = (!isStatic && !isGradient && !isColorCycle)
|
||||
const icon = isStatic ? '🎨' : isColorCycle ? '🔄' : isGradient ? '🌈' : isEffect ? '⚡' : '🎞️';
|
||||
const calibrationBtn = (!isStatic && !isGradient && !isColorCycle && !isEffect)
|
||||
? `<button class="btn btn-icon btn-secondary" onclick="showCSSCalibration('${source.id}')" title="${t('calibration.title')}">📐</button>`
|
||||
: '';
|
||||
|
||||
@@ -319,11 +364,24 @@ export async function showCSSEditor(cssId = null) {
|
||||
} else if (sourceType === 'color_cycle') {
|
||||
_loadColorCycleState(css);
|
||||
} else if (sourceType === 'gradient') {
|
||||
document.getElementById('css-editor-gradient-preset').value = '';
|
||||
gradientInit(css.stops || [
|
||||
{ position: 0.0, color: [255, 0, 0] },
|
||||
{ position: 1.0, color: [0, 0, 255] },
|
||||
]);
|
||||
_loadAnimationState(css.animation);
|
||||
} else if (sourceType === 'effect') {
|
||||
document.getElementById('css-editor-effect-type').value = css.effect_type || 'fire';
|
||||
onEffectTypeChange();
|
||||
document.getElementById('css-editor-effect-speed').value = css.speed ?? 1.0;
|
||||
document.getElementById('css-editor-effect-speed-val').textContent = parseFloat(css.speed ?? 1.0).toFixed(1);
|
||||
document.getElementById('css-editor-effect-palette').value = css.palette || 'fire';
|
||||
document.getElementById('css-editor-effect-color').value = rgbArrayToHex(css.color || [255, 80, 0]);
|
||||
document.getElementById('css-editor-effect-intensity').value = css.intensity ?? 1.0;
|
||||
document.getElementById('css-editor-effect-intensity-val').textContent = parseFloat(css.intensity ?? 1.0).toFixed(1);
|
||||
document.getElementById('css-editor-effect-scale').value = css.scale ?? 1.0;
|
||||
document.getElementById('css-editor-effect-scale-val').textContent = parseFloat(css.scale ?? 1.0).toFixed(1);
|
||||
document.getElementById('css-editor-effect-mirror').checked = css.mirror || false;
|
||||
} else {
|
||||
sourceSelect.value = css.picture_source_id || '';
|
||||
|
||||
@@ -369,7 +427,18 @@ export async function showCSSEditor(cssId = null) {
|
||||
document.getElementById('css-editor-led-count').value = 0;
|
||||
_loadAnimationState(null);
|
||||
_loadColorCycleState(null);
|
||||
document.getElementById('css-editor-effect-type').value = 'fire';
|
||||
document.getElementById('css-editor-effect-speed').value = 1.0;
|
||||
document.getElementById('css-editor-effect-speed-val').textContent = '1.0';
|
||||
document.getElementById('css-editor-effect-palette').value = 'fire';
|
||||
document.getElementById('css-editor-effect-color').value = '#ff5000';
|
||||
document.getElementById('css-editor-effect-intensity').value = 1.0;
|
||||
document.getElementById('css-editor-effect-intensity-val').textContent = '1.0';
|
||||
document.getElementById('css-editor-effect-scale').value = 1.0;
|
||||
document.getElementById('css-editor-effect-scale-val').textContent = '1.0';
|
||||
document.getElementById('css-editor-effect-mirror').checked = false;
|
||||
document.getElementById('css-editor-title').textContent = t('color_strip.add');
|
||||
document.getElementById('css-editor-gradient-preset').value = '';
|
||||
gradientInit([
|
||||
{ position: 0.0, color: [255, 0, 0] },
|
||||
{ position: 1.0, color: [0, 0, 255] },
|
||||
@@ -438,6 +507,23 @@ export async function saveCSSEditor() {
|
||||
animation: _getAnimationPayload(),
|
||||
};
|
||||
if (!cssId) payload.source_type = 'gradient';
|
||||
} else if (sourceType === 'effect') {
|
||||
payload = {
|
||||
name,
|
||||
effect_type: document.getElementById('css-editor-effect-type').value,
|
||||
speed: parseFloat(document.getElementById('css-editor-effect-speed').value),
|
||||
palette: document.getElementById('css-editor-effect-palette').value,
|
||||
intensity: parseFloat(document.getElementById('css-editor-effect-intensity').value),
|
||||
scale: parseFloat(document.getElementById('css-editor-effect-scale').value),
|
||||
mirror: document.getElementById('css-editor-effect-mirror').checked,
|
||||
led_count: parseInt(document.getElementById('css-editor-led-count').value) || 0,
|
||||
};
|
||||
// Meteor uses a color picker
|
||||
if (payload.effect_type === 'meteor') {
|
||||
const hex = document.getElementById('css-editor-effect-color').value;
|
||||
payload.color = [parseInt(hex.slice(1, 3), 16), parseInt(hex.slice(3, 5), 16), parseInt(hex.slice(5, 7), 16)];
|
||||
}
|
||||
if (!cssId) payload.source_type = 'effect';
|
||||
} else {
|
||||
payload = {
|
||||
name,
|
||||
@@ -596,6 +682,101 @@ export function gradientInit(stops) {
|
||||
gradientRenderAll();
|
||||
}
|
||||
|
||||
/* ── Presets ──────────────────────────────────────────────────── */
|
||||
|
||||
const _GRADIENT_PRESETS = {
|
||||
rainbow: [
|
||||
{ position: 0.0, color: [255, 0, 0] },
|
||||
{ position: 0.17, color: [255, 165, 0] },
|
||||
{ position: 0.33, color: [255, 255, 0] },
|
||||
{ position: 0.5, color: [0, 255, 0] },
|
||||
{ position: 0.67, color: [0, 100, 255] },
|
||||
{ position: 0.83, color: [75, 0, 130] },
|
||||
{ position: 1.0, color: [148, 0, 211] },
|
||||
],
|
||||
sunset: [
|
||||
{ position: 0.0, color: [255, 60, 0] },
|
||||
{ position: 0.3, color: [255, 120, 20] },
|
||||
{ position: 0.6, color: [200, 40, 80] },
|
||||
{ position: 0.8, color: [120, 20, 120] },
|
||||
{ position: 1.0, color: [40, 10, 60] },
|
||||
],
|
||||
ocean: [
|
||||
{ position: 0.0, color: [0, 10, 40] },
|
||||
{ position: 0.3, color: [0, 60, 120] },
|
||||
{ position: 0.6, color: [0, 140, 180] },
|
||||
{ position: 0.8, color: [100, 220, 240] },
|
||||
{ position: 1.0, color: [200, 240, 255] },
|
||||
],
|
||||
forest: [
|
||||
{ position: 0.0, color: [0, 40, 0] },
|
||||
{ position: 0.3, color: [0, 100, 20] },
|
||||
{ position: 0.6, color: [60, 180, 30] },
|
||||
{ position: 0.8, color: [140, 220, 50] },
|
||||
{ position: 1.0, color: [220, 255, 80] },
|
||||
],
|
||||
fire: [
|
||||
{ position: 0.0, color: [0, 0, 0] },
|
||||
{ position: 0.25, color: [80, 0, 0] },
|
||||
{ position: 0.5, color: [255, 40, 0] },
|
||||
{ position: 0.75, color: [255, 160, 0] },
|
||||
{ position: 1.0, color: [255, 255, 60] },
|
||||
],
|
||||
lava: [
|
||||
{ position: 0.0, color: [0, 0, 0] },
|
||||
{ position: 0.3, color: [120, 0, 0] },
|
||||
{ position: 0.6, color: [255, 60, 0] },
|
||||
{ position: 0.8, color: [255, 160, 40] },
|
||||
{ position: 1.0, color: [255, 255, 120] },
|
||||
],
|
||||
aurora: [
|
||||
{ position: 0.0, color: [0, 20, 40] },
|
||||
{ position: 0.25, color: [0, 200, 100] },
|
||||
{ position: 0.5, color: [0, 100, 200] },
|
||||
{ position: 0.75, color: [120, 0, 200] },
|
||||
{ position: 1.0, color: [0, 200, 140] },
|
||||
],
|
||||
ice: [
|
||||
{ position: 0.0, color: [255, 255, 255] },
|
||||
{ position: 0.3, color: [180, 220, 255] },
|
||||
{ position: 0.6, color: [80, 160, 255] },
|
||||
{ position: 0.85, color: [20, 60, 180] },
|
||||
{ position: 1.0, color: [10, 20, 80] },
|
||||
],
|
||||
warm: [
|
||||
{ position: 0.0, color: [255, 255, 80] },
|
||||
{ position: 0.33, color: [255, 160, 0] },
|
||||
{ position: 0.67, color: [255, 60, 0] },
|
||||
{ position: 1.0, color: [160, 0, 0] },
|
||||
],
|
||||
cool: [
|
||||
{ position: 0.0, color: [0, 255, 200] },
|
||||
{ position: 0.33, color: [0, 120, 255] },
|
||||
{ position: 0.67, color: [60, 0, 255] },
|
||||
{ position: 1.0, color: [120, 0, 180] },
|
||||
],
|
||||
neon: [
|
||||
{ position: 0.0, color: [255, 0, 200] },
|
||||
{ position: 0.25, color: [0, 255, 255] },
|
||||
{ position: 0.5, color: [0, 255, 50] },
|
||||
{ position: 0.75, color: [255, 255, 0] },
|
||||
{ position: 1.0, color: [255, 0, 100] },
|
||||
],
|
||||
pastel: [
|
||||
{ position: 0.0, color: [255, 180, 180] },
|
||||
{ position: 0.2, color: [255, 220, 160] },
|
||||
{ position: 0.4, color: [255, 255, 180] },
|
||||
{ position: 0.6, color: [180, 255, 200] },
|
||||
{ position: 0.8, color: [180, 200, 255] },
|
||||
{ position: 1.0, color: [220, 180, 255] },
|
||||
],
|
||||
};
|
||||
|
||||
export function applyGradientPreset(key) {
|
||||
if (!key || !_GRADIENT_PRESETS[key]) return;
|
||||
gradientInit(_GRADIENT_PRESETS[key]);
|
||||
}
|
||||
|
||||
/* ── Render ───────────────────────────────────────────────────── */
|
||||
|
||||
export function gradientRenderAll() {
|
||||
|
||||
@@ -589,6 +589,21 @@
|
||||
"color_strip.gradient.position": "Position (0.0–1.0)",
|
||||
"color_strip.gradient.bidir.hint": "Add a second color on the right side of this stop to create a hard edge in the gradient.",
|
||||
"color_strip.gradient.min_stops": "Gradient must have at least 2 stops",
|
||||
"color_strip.gradient.preset": "Preset:",
|
||||
"color_strip.gradient.preset.hint": "Load a predefined gradient palette. Selecting a preset replaces the current stops.",
|
||||
"color_strip.gradient.preset.custom": "— Custom —",
|
||||
"color_strip.gradient.preset.rainbow": "Rainbow",
|
||||
"color_strip.gradient.preset.sunset": "Sunset",
|
||||
"color_strip.gradient.preset.ocean": "Ocean",
|
||||
"color_strip.gradient.preset.forest": "Forest",
|
||||
"color_strip.gradient.preset.fire": "Fire",
|
||||
"color_strip.gradient.preset.lava": "Lava",
|
||||
"color_strip.gradient.preset.aurora": "Aurora",
|
||||
"color_strip.gradient.preset.ice": "Ice",
|
||||
"color_strip.gradient.preset.warm": "Warm",
|
||||
"color_strip.gradient.preset.cool": "Cool",
|
||||
"color_strip.gradient.preset.neon": "Neon",
|
||||
"color_strip.gradient.preset.pastel": "Pastel",
|
||||
"color_strip.animation": "Animation",
|
||||
"color_strip.animation.type": "Effect:",
|
||||
"color_strip.animation.type.hint": "Animation effect to apply.",
|
||||
@@ -617,5 +632,39 @@
|
||||
"color_strip.color_cycle.add_color": "+ Add Color",
|
||||
"color_strip.color_cycle.speed": "Speed:",
|
||||
"color_strip.color_cycle.speed.hint": "Cycle speed multiplier. 1.0 ≈ one full cycle every 20 seconds; higher values cycle faster.",
|
||||
"color_strip.color_cycle.min_colors": "Color cycle must have at least 2 colors"
|
||||
"color_strip.color_cycle.min_colors": "Color cycle must have at least 2 colors",
|
||||
"color_strip.type.effect": "Effect",
|
||||
"color_strip.type.effect.hint": "Procedural LED effects (fire, meteor, plasma, noise, aurora) generated in real time.",
|
||||
"color_strip.effect.type": "Effect Type:",
|
||||
"color_strip.effect.type.hint": "Choose the procedural algorithm.",
|
||||
"color_strip.effect.fire": "Fire",
|
||||
"color_strip.effect.fire.desc": "Cellular automaton simulating rising flames with heat diffusion",
|
||||
"color_strip.effect.meteor": "Meteor",
|
||||
"color_strip.effect.meteor.desc": "Bright head travels along the strip with an exponential-decay tail",
|
||||
"color_strip.effect.plasma": "Plasma",
|
||||
"color_strip.effect.plasma.desc": "Overlapping sine waves mapped to a palette — classic demo-scene effect",
|
||||
"color_strip.effect.noise": "Noise",
|
||||
"color_strip.effect.noise.desc": "Scrolling fractal value noise mapped to a palette",
|
||||
"color_strip.effect.aurora": "Aurora",
|
||||
"color_strip.effect.aurora.desc": "Layered noise bands that drift and blend — northern lights style",
|
||||
"color_strip.effect.speed": "Speed:",
|
||||
"color_strip.effect.speed.hint": "Speed multiplier for the effect animation (0.1 = very slow, 10.0 = very fast).",
|
||||
"color_strip.effect.palette": "Palette:",
|
||||
"color_strip.effect.palette.hint": "Color palette used to map effect values to RGB colors.",
|
||||
"color_strip.effect.color": "Meteor Color:",
|
||||
"color_strip.effect.color.hint": "Head color for the meteor effect.",
|
||||
"color_strip.effect.intensity": "Intensity:",
|
||||
"color_strip.effect.intensity.hint": "Effect intensity — controls spark rate (fire), tail decay (meteor), or brightness range (aurora).",
|
||||
"color_strip.effect.scale": "Scale:",
|
||||
"color_strip.effect.scale.hint": "Spatial scale — wave frequency (plasma), zoom level (noise), or band width (aurora).",
|
||||
"color_strip.effect.mirror": "Mirror:",
|
||||
"color_strip.effect.mirror.hint": "Bounce mode — the meteor reverses direction at strip ends instead of wrapping.",
|
||||
"color_strip.palette.fire": "Fire",
|
||||
"color_strip.palette.ocean": "Ocean",
|
||||
"color_strip.palette.lava": "Lava",
|
||||
"color_strip.palette.forest": "Forest",
|
||||
"color_strip.palette.rainbow": "Rainbow",
|
||||
"color_strip.palette.aurora": "Aurora",
|
||||
"color_strip.palette.sunset": "Sunset",
|
||||
"color_strip.palette.ice": "Ice"
|
||||
}
|
||||
|
||||
@@ -589,6 +589,21 @@
|
||||
"color_strip.gradient.position": "Позиция (0.0–1.0)",
|
||||
"color_strip.gradient.bidir.hint": "Добавить второй цвет справа от этой остановки для создания резкого перехода в градиенте.",
|
||||
"color_strip.gradient.min_stops": "Градиент должен содержать не менее 2 остановок",
|
||||
"color_strip.gradient.preset": "Пресет:",
|
||||
"color_strip.gradient.preset.hint": "Загрузить готовую палитру градиента. Выбор пресета заменяет текущие остановки.",
|
||||
"color_strip.gradient.preset.custom": "— Свой —",
|
||||
"color_strip.gradient.preset.rainbow": "Радуга",
|
||||
"color_strip.gradient.preset.sunset": "Закат",
|
||||
"color_strip.gradient.preset.ocean": "Океан",
|
||||
"color_strip.gradient.preset.forest": "Лес",
|
||||
"color_strip.gradient.preset.fire": "Огонь",
|
||||
"color_strip.gradient.preset.lava": "Лава",
|
||||
"color_strip.gradient.preset.aurora": "Аврора",
|
||||
"color_strip.gradient.preset.ice": "Лёд",
|
||||
"color_strip.gradient.preset.warm": "Тёплый",
|
||||
"color_strip.gradient.preset.cool": "Холодный",
|
||||
"color_strip.gradient.preset.neon": "Неон",
|
||||
"color_strip.gradient.preset.pastel": "Пастельный",
|
||||
"color_strip.animation": "Анимация",
|
||||
"color_strip.animation.type": "Эффект:",
|
||||
"color_strip.animation.type.hint": "Эффект анимации.",
|
||||
@@ -617,5 +632,39 @@
|
||||
"color_strip.color_cycle.add_color": "+ Добавить цвет",
|
||||
"color_strip.color_cycle.speed": "Скорость:",
|
||||
"color_strip.color_cycle.speed.hint": "Множитель скорости смены. 1.0 ≈ один полный цикл за 20 секунд; большие значения ускоряют смену.",
|
||||
"color_strip.color_cycle.min_colors": "Смена цвета должна содержать не менее 2 цветов"
|
||||
"color_strip.color_cycle.min_colors": "Смена цвета должна содержать не менее 2 цветов",
|
||||
"color_strip.type.effect": "Эффект",
|
||||
"color_strip.type.effect.hint": "Процедурные LED-эффекты (огонь, метеор, плазма, шум, аврора), генерируемые в реальном времени.",
|
||||
"color_strip.effect.type": "Тип эффекта:",
|
||||
"color_strip.effect.type.hint": "Выберите процедурный алгоритм.",
|
||||
"color_strip.effect.fire": "Огонь",
|
||||
"color_strip.effect.fire.desc": "Клеточный автомат, имитирующий поднимающееся пламя с диффузией тепла",
|
||||
"color_strip.effect.meteor": "Метеор",
|
||||
"color_strip.effect.meteor.desc": "Яркая точка движется по ленте с экспоненциально затухающим хвостом",
|
||||
"color_strip.effect.plasma": "Плазма",
|
||||
"color_strip.effect.plasma.desc": "Наложение синусоидальных волн с палитрой — классический демо-эффект",
|
||||
"color_strip.effect.noise": "Шум",
|
||||
"color_strip.effect.noise.desc": "Прокручиваемый фрактальный шум, отображённый на палитру",
|
||||
"color_strip.effect.aurora": "Аврора",
|
||||
"color_strip.effect.aurora.desc": "Наложенные шумовые полосы, дрейфующие и смешивающиеся — в стиле северного сияния",
|
||||
"color_strip.effect.speed": "Скорость:",
|
||||
"color_strip.effect.speed.hint": "Множитель скорости анимации эффекта (0.1 = очень медленно, 10.0 = очень быстро).",
|
||||
"color_strip.effect.palette": "Палитра:",
|
||||
"color_strip.effect.palette.hint": "Цветовая палитра для отображения значений эффекта в RGB-цвета.",
|
||||
"color_strip.effect.color": "Цвет метеора:",
|
||||
"color_strip.effect.color.hint": "Цвет головной точки метеора.",
|
||||
"color_strip.effect.intensity": "Интенсивность:",
|
||||
"color_strip.effect.intensity.hint": "Интенсивность эффекта — частота искр (огонь), затухание хвоста (метеор) или диапазон яркости (аврора).",
|
||||
"color_strip.effect.scale": "Масштаб:",
|
||||
"color_strip.effect.scale.hint": "Пространственный масштаб — частота волн (плазма), уровень масштабирования (шум) или ширина полос (аврора).",
|
||||
"color_strip.effect.mirror": "Отражение:",
|
||||
"color_strip.effect.mirror.hint": "Режим отскока — метеор меняет направление у краёв ленты вместо переноса.",
|
||||
"color_strip.palette.fire": "Огонь",
|
||||
"color_strip.palette.ocean": "Океан",
|
||||
"color_strip.palette.lava": "Лава",
|
||||
"color_strip.palette.forest": "Лес",
|
||||
"color_strip.palette.rainbow": "Радуга",
|
||||
"color_strip.palette.aurora": "Аврора",
|
||||
"color_strip.palette.sunset": "Закат",
|
||||
"color_strip.palette.ice": "Лёд"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user