UNPKG

@lightningtv/renderer

Version:
228 lines (191 loc) 5.67 kB
/* * If not stated otherwise in this file or this component's LICENSE file the * following copyright and licenses apply: * * Copyright 2023 Comcast Cable Communications Management, LLC. * * Licensed under the Apache License, Version 2.0 (the License); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * Core Utility Functions * * @module */ export const EPSILON = 0.000001; export let ARRAY_TYPE = typeof Float32Array !== 'undefined' ? Float32Array : Array; export const RANDOM = Math.random; export const ANGLE_ORDER = 'zyx'; const degree = Math.PI / 180; export const setMatrixArrayType = ( type: Float32ArrayConstructor | ArrayConstructor, ) => { ARRAY_TYPE = type; }; export const toRadian = (a: number) => { return a * degree; }; export const equals = (a: number, b: number) => { return Math.abs(a - b) <= EPSILON * Math.max(1.0, Math.abs(a), Math.abs(b)); }; export const rand = (min: number, max: number) => { return Math.floor(Math.random() * (max - min + 1) + min); }; export const isPowerOfTwo = (value: number) => { return value && !(value & (value - 1)); }; const getTimingBezier = ( a: number, b: number, c: number, d: number, ): ((time: number) => number | undefined) => { const xc = 3.0 * a; const xb = 3.0 * (c - a) - xc; const xa = 1.0 - xc - xb; const yc = 3.0 * b; const yb = 3.0 * (d - b) - yc; const ya = 1.0 - yc - yb; return function (time: number): number | undefined { if (time >= 1.0) { return 1; } if (time <= 0) { return 0; } let t = 0.5, cbx, cbxd, dx; for (let it = 0; it < 20; it++) { cbx = t * (t * (t * xa + xb) + xc); dx = time - cbx; if (dx > -1e-8 && dx < 1e-8) { return t * (t * (t * ya + yb) + yc); } // Cubic bezier derivative. cbxd = t * (t * (3 * xa) + 2 * xb) + xc; if (cbxd > 1e-10 && cbxd < 1e-10) { // Problematic. Fall back to binary search method. break; } t += dx / cbxd; } // Fallback: binary search method. This is more reliable when there are near-0 slopes. let minT = 0; let maxT = 1; for (let it = 0; it < 20; it++) { t = 0.5 * (minT + maxT); cbx = t * (t * (t * xa + xb) + xc); dx = time - cbx; if (dx > -1e-8 && dx < 1e-8) { // Solution found! return t * (t * (t * ya + yb) + yc); } if (dx < 0) { maxT = t; } else { minT = t; } } }; }; interface TimingFunctionMap { [key: string]: (time: number) => number | undefined; } type TimingLookupArray = number[]; interface TimingLookup { [key: string]: TimingLookupArray; } const timingMapping: TimingFunctionMap = {}; const timingLookup: TimingLookup = { ease: [0.25, 0.1, 0.25, 1.0], 'ease-in': [0.42, 0, 1.0, 1.0], 'ease-out': [0, 0, 0.58, 1.0], 'ease-in-out': [0.42, 0, 0.58, 1.0], 'ease-in-sine': [0.12, 0, 0.39, 0], 'ease-out-sine': [0.12, 0, 0.39, 0], 'ease-in-out-sine': [0.37, 0, 0.63, 1], 'ease-in-cubic': [0.32, 0, 0.67, 0], 'ease-out-cubic': [0.33, 1, 0.68, 1], 'ease-in-out-cubic': [0.65, 0, 0.35, 1], 'ease-in-circ': [0.55, 0, 1, 0.45], 'ease-out-circ': [0, 0.55, 0.45, 1], 'ease-in-out-circ': [0.85, 0, 0.15, 1], 'ease-in-back': [0.36, 0, 0.66, -0.56], 'ease-out-back': [0.34, 1.56, 0.64, 1], 'ease-in-out-back': [0.68, -0.6, 0.32, 1.6], }; const defaultTiming = (t: number): number => t; const parseCubicBezier = (str: string) => { //cubic-bezier(0.84, 0.52, 0.56, 0.6) const regex = /-?\d*\.?\d+/g; const match = str.match(regex); if (match) { const [num1, num2, num3, num4] = match; const a = parseFloat(num1 || '0.42'); const b = parseFloat(num2 || '0'); const c = parseFloat(num3 || '1'); const d = parseFloat(num4 || '1'); const timing = getTimingBezier(a, b, c, d); timingMapping[str] = timing; return timing; } // parse failed, return linear console.warn('Unknown cubic-bezier timing: ' + str); return defaultTiming; }; export const getTimingFunction = ( str: string, ): ((time: number) => number | undefined) => { if (str === 'linear') { return defaultTiming; } if (timingMapping[str] !== undefined) { return timingMapping[str] || defaultTiming; } if (str === 'step-start') { return () => { return 1; }; } if (str === 'step-end') { return (time: number) => { return time === 1 ? 1 : 0; }; } const lookup = timingLookup[str]; if (lookup !== undefined) { const [a, b, c, d] = lookup; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - TS doesn't understand that we've checked for undefined // eslint-disable-next-line @typescript-eslint/no-unsafe-argument const timing = getTimingBezier(a, b, c, d); timingMapping[str] = timing; return timing; } if (str.startsWith('cubic-bezier')) { return parseCubicBezier(str); } console.warn('Unknown timing function: ' + str); return defaultTiming; }; /** * Convert bytes to string of megabytes with 2 decimal points * * @param bytes * @returns */ export function bytesToMb(bytes: number) { return (bytes / 1024 / 1024).toFixed(2); }