UNPKG

@ginden/blinkstick-v2

Version:
97 lines 4.86 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.morphBetweenComplexFrames = morphBetweenComplexFrames; exports.morphComplex = morphComplex; const assert_1 = __importDefault(require("assert")); const complex_frame_1 = require("../frame/complex-frame"); const simple_frame_1 = require("../frame/simple-frame"); const wait_frame_1 = require("../frame/wait-frame"); const morph_1 = require("./morph"); const utils_1 = require("../../utils"); const assert_fps_below_100_1 = require("../helpers/assert-fps-below-100"); /** * Morphs between two complex frames. * @category Animations */ function morphBetweenComplexFrames(firstFrameAsComplexFrame, lastFrameAsComplexFrame, overMs, stepsRaw) { const steps = (stepsRaw ?? overMs / 60) | 0; (0, assert_1.default)(steps > 0, 'Steps must be greater than 0'); (0, assert_1.default)(steps * 10 < overMs, 'Frame FPS is too high, please reduce steps or increase overMs'); return { *[Symbol.iterator]() { const firstFrame = firstFrameAsComplexFrame.colors; const lastFrame = lastFrameAsComplexFrame.colors; const stepDuration = overMs / steps; for (let i = 0; i < steps; i++) { const progress = (i + 1) / (steps + 1); const colors = firstFrame.map((color, ledIndex) => { return color.map((component, componentIndex) => { const start = component; const end = lastFrame[ledIndex][componentIndex]; const interpolated = start + progress * (end - start); return (0, utils_1.clampRgb)(interpolated); }); }); yield new complex_frame_1.ComplexFrame(colors, stepDuration); } }, }; } /** * Smooth transition between two animations. */ function morphComplex(source, target, overMs, stepsRaw) { // Default to ~30 fps to avoid sending excessive number of HID reports on // real BlinkStick hardware (USB latency makes >30 fps impractical). const steps = (stepsRaw ?? overMs / 33) | 0; // ≈ 30 frames-per-second (0, assert_1.default)(steps > 0, 'Steps must be greater than 0'); (0, assert_fps_below_100_1.assertFpsBelow100)(overMs, steps); return { [Symbol.asyncIterator]: async function* () { let lastSourceFrame = undefined; for await (const frame of source) { yield frame; if (frame instanceof wait_frame_1.WaitFrame) { continue; } lastSourceFrame = frame; } (0, assert_1.default)(lastSourceFrame, 'Source animation is empty'); // We do this, because generator[Symbol.asyncIterator]() === generator - there is no "forking" // Therefore, we can take the first frame from the target animation and then yield all other frames const targetAsGenerator = (async function* () { yield* target; })(); const { value: firstTargetFrame } = await targetAsGenerator.next(); (0, assert_1.default)(firstTargetFrame, 'Target animation is empty'); // MORPH LOGIC HERE if (firstTargetFrame instanceof wait_frame_1.WaitFrame) { throw new Error('Cannot morph to a NullFrame'); } // Give me pattern matching plz if (firstTargetFrame instanceof simple_frame_1.SimpleFrame && lastSourceFrame instanceof simple_frame_1.SimpleFrame) { yield* (0, morph_1.morph)(lastSourceFrame.rgb, firstTargetFrame.rgb, overMs, steps); } else { const ledCount = [firstTargetFrame, lastSourceFrame] .filter((v) => v instanceof complex_frame_1.ComplexFrame) .pop().colors.length; const firstFrameAsComplexFrame = firstTargetFrame instanceof complex_frame_1.ComplexFrame ? firstTargetFrame : complex_frame_1.ComplexFrame.fromSimpleFrame(firstTargetFrame, ledCount); const lastFrameAsComplexFrame = lastSourceFrame instanceof complex_frame_1.ComplexFrame ? lastSourceFrame : complex_frame_1.ComplexFrame.fromSimpleFrame(lastSourceFrame, ledCount); yield* morphBetweenComplexFrames(firstFrameAsComplexFrame, lastFrameAsComplexFrame, overMs); } yield firstTargetFrame; for await (const frame of targetAsGenerator) { yield frame; } }, }; } //# sourceMappingURL=morph-complex.js.map