UNPKG

@bannsaenger/node-red-contrib-artnet-controller

Version:

Node-RED node that controls lights via Art-Net. Acts as a Art-Net controller.

240 lines (192 loc) 8.89 kB
class LinearTransition { /** * Compute single step for linear curve transition * @param {number} startValue - value where the transition starts * @param {number} targetValue - value to which compute values/steps * @param {number} stepCount - count of steps in transition * @param {number} currentStep - current step in transition * @returns {number} value for current step */ computeValue (startValue, targetValue, stepCount, currentStep) { // should we fade up or down? const valuePerStep = (targetValue - startValue) / stepCount; const currentValue = (valuePerStep * currentStep) + startValue return Math.round(currentValue) } /** * Compute steps for linear curve transition * @param {number} duration - duration of transition * @param {number} stepCount - count of steps in transition * @param {number} oldValue - value where the transition starts * @param {number} newValue - value to which compute values/steps * @returns values for all steps */ computeValues (duration, stepCount, oldValue, newValue) { const steps = [] // should we fade up or down? const valuePerStep = (newValue - oldValue) / stepCount; const stepDuration = duration / stepCount; for (let i = 1; i <= stepCount; i++) { const iterationValue = oldValue + i * valuePerStep; steps[i] = { 'value' : Math.round(iterationValue), 'time' : i * stepDuration, 'step' : i }; } return steps } } class GammaTransition { constructor(gammaFactor = 2.2) { this.gammaFactor = gammaFactor } /** * Compute single step for gamma curve transition. Gamma curve takes advantage of human perception * @param {number} startValue - value where the transition starts * @param {number} targetValue - value to which compute values/steps * @param {number} stepCount - count of steps in transition * @param {number} currentStep - current step in transition * @param {number} [gammaFactor] - if you want another gamma * @returns {number} value for current step */ computeValue (startValue, targetValue, stepCount, currentStep, gammaFactor = 2.2) { // Apply gamma-like interpolation to make the transition non-linear const gammaProgress = Math.pow(currentStep / stepCount, gammaFactor); // Interpolate between oldValue and newValue based on the gamma progress const interpolatedValue = startValue + (targetValue - startValue) * gammaProgress; return Math.round(interpolatedValue) } /** * Compute all steps for gamma curve transition. Gamma curve takes advantage of human perception * of changes in intensity in higher brightness values. * @param {number} duration - duration of transition * @param {number} stepCount - count of steps in transition * @param {number} oldValue - value where the transition starts * @param {number} newValue - value to which compute values/steps * @returns values for all steps */ computeValues (duration, stepCount, oldValue, newValue) { const steps = []; const stepDuration = duration / stepCount; for (let i = 0; i <= stepCount; i++) { // Normalize the time to a value between 0 and 1 based on the step's progress const stepProgress = i / stepCount; // Apply gamma-like interpolation to make the transition non-linear const gammaProgress = Math.pow(stepProgress, this.gammaFactor); // Interpolate between oldValue and newValue based on the gamma progress const interpolatedValue = oldValue + (newValue - oldValue) * gammaProgress; steps[i] = { 'value' : Math.round(interpolatedValue), 'time' : i * stepDuration, 'step' : i }; } return steps; } } class QuadraticTransition { /** * Compute single step for gamma curve transition. Gamma curve takes advantage of human perception * @param {number} startValue - value where the transition starts * @param {number} targetValue - value to which compute values/steps * @param {number} stepCount - count of steps in transition * @param {number} currentStep - current step in transition * @returns {number} value for current step */ computeValue (startValue, targetValue, stepCount, currentStep) { // Normalize the step to a value between 0 and 1 const stepProgress = currentStep / stepCount; // Apply quadratic interpolation (slow-to-fast) const quadraticProgress = stepProgress * stepProgress; // Interpolate between oldValue and newValue based on the gamma progress const interpolatedValue = startValue + (targetValue - startValue) * quadraticProgress; return Math.round(interpolatedValue) } /** * Compute all steps for quadratic curve transition. * @param {number} duration - duration of transition * @param {number} stepCount - count of steps in transition * @param {number} oldValue - value where the transition starts * @param {number} newValue - value to which compute values/steps * @returns values for all steps */ computeValues (duration, stepCount, oldValue, newValue) { const steps = []; const stepDuration = duration / stepCount; for (let i = 0; i <= stepCount; i++) { // Normalize the step to a value between 0 and 1 const stepProgress = i / stepCount; // Apply quadratic interpolation (slow-to-fast) const quadraticProgress = stepProgress * stepProgress; // Interpolate between oldValue and newValue based on the quadratic progress const interpolatedValue = oldValue + (newValue - oldValue) * quadraticProgress; steps[i] = { 'value' : Math.round(interpolatedValue), 'time' : i * stepDuration, 'step' : i }; } return steps; } } class SineTransition { /** * Compute single steps for sine curve transition * @param {number} startValue - value where the transition starts * @param {number} targetValue - value to which compute values/steps * @param {number} stepCount - count of steps in transition * @param {number} currentStep - current step in transition * @returns {number} value for current step */ computeValue (startValue, targetValue, stepCount, currentStep) { // Normalize the step to a value between 0 and PI const stepProgress = (currentStep / stepCount) * Math.PI; // Apply sine starting at -PI/2 elevated by 1 and then half to have values from 0 to 1 const sineProgress = (Math.sin(stepProgress - (Math.PI / 2)) + 1) / 2; // Interpolate between oldValue and newValue based on the sine progress const interpolatedValue = startValue + (targetValue - startValue) * sineProgress; return Math.round(interpolatedValue) } /** * Compute all steps for sine curve transition * @param {number} duration - duration of transition * @param {number} stepCount - count of steps in transition * @param {number} oldValue - value where the transition starts * @param {number} newValue - value to which compute values/steps * @returns values for all steps */ computeValues (duration, stepCount, oldValue, newValue) { const steps = []; const stepDuration = duration / stepCount; // get the radians per step based on 90 degrees per trsansaction const radPerStep = (90 / stepCount) * Math.PI / 180; for (let i = 0; i <= stepCount; i++) { steps[i] = { 'value' : Math.round(Math.sin(radPerStep * i) * newValue), 'time' : i * stepDuration, 'step' : i }; } return steps; } } /** * * @param {string} type * @returns */ module.exports.TransitionFactory = function(type) { switch (type) { case 'linear': return new LinearTransition(); case 'gamma': return new GammaTransition(); case 'quadratic': return new QuadraticTransition(); case 'sine': return new SineTransition(); default: throw new Error("Unknown transtition type: " + type); } }