// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. import Animated, {useSharedValue} from 'react-native-reanimated'; type SharedValueType = number; type Callback = () => unknown; type VectorType = Vector | SharedVector; type VectorList = Array; type Operation = 'divide' | 'add' | 'sub' | 'multiply'; type VectorProp = 'x' | 'y'; export type Vector = { x: T; y: T; }; export type SharedVector = { x: Animated.SharedValue; y: Animated.SharedValue; }; const isVector = (value: any): value is Vector => { 'worklet'; return value.x !== undefined && value.y !== undefined; }; const isSharedValue = ( value: any, ): value is Animated.SharedValue => { 'worklet'; return typeof value.value !== 'undefined'; }; const get = | SharedValueType>( value: T, ) => { 'worklet'; if (isSharedValue(value)) { return value.value; } return value; }; const reduce = ( operation: Operation, prop: VectorProp, vectors: VectorList, ) => { 'worklet'; const first = vectors[0]; const rest = vectors.slice(1); const initial = get(isVector(first) ? first[prop] : first); const res = rest.reduce((acc, current) => { const value = get(isVector(current) ? current[prop] : current); const r = (() => { switch (operation) { case 'divide': if (value === 0) { return 0; } return acc / value; case 'add': return acc + value; case 'sub': return acc - value; case 'multiply': return acc * value; default: return acc; } })(); return r; }, initial); return res; }; export const useSharedVector = (x: T, y = x) => { return { x: useSharedValue(x), y: useSharedValue(y), }; }; export const create = (x: T, y: T) => { 'worklet'; return { x, y, }; }; export const add = (...vectors: VectorList) => { 'worklet'; return { x: reduce('add', 'x', vectors), y: reduce('add', 'y', vectors), }; }; export const sub = (...vectors: VectorList) => { 'worklet'; return { x: reduce('sub', 'x', vectors), y: reduce('sub', 'y', vectors), }; }; export const divide = (...vectors: VectorList) => { 'worklet'; return { x: reduce('divide', 'x', vectors), y: reduce('divide', 'y', vectors), }; }; export const multiply = (...vectors: VectorList) => { 'worklet'; return { x: reduce('multiply', 'x', vectors), y: reduce('multiply', 'y', vectors), }; }; export const invert = >(vector: T) => { 'worklet'; return { x: get(vector.x) * -1, y: get(vector.y) * -1, }; }; export const set = ( vector: T, value: VectorType | SharedValueType | Callback, ) => { 'worklet'; // handle animation if (typeof value === 'function') { vector.x.value = value(); vector.y.value = value(); } const x = get(isVector(value) ? value.x : value); const y = get(isVector(value) ? value.y : value); if (typeof vector.x.value === 'undefined') { vector.x = x; vector.y = y; } else { vector.x.value = x; vector.y.value = y; } }; export const min = (...vectors: VectorList) => { 'worklet'; const getMin = (prop: VectorProp) => { return Math.min.apply( undefined, vectors.map((item) => get(isVector(item) ? item[prop] : item), ), ); }; return { x: getMin('x'), y: getMin('y'), }; }; export const max = (...vectors: VectorList) => { 'worklet'; const getMax = (prop: VectorProp) => Math.max.apply( undefined, vectors.map((item) => get(isVector(item) ? item[prop] : item), ), ); return { x: getMax('x'), y: getMax('y'), }; }; export const clamp = >( value: T, lowerBound: VectorType | SharedValueType, upperBound: VectorType | SharedValueType, ) => { 'worklet'; return min(max(lowerBound, value), upperBound); }; export const eq = >( vector: T, value: VectorType | SharedValueType, ) => { 'worklet'; const x = get(isVector(value) ? value.x : value); const y = get(isVector(value) ? value.y : value); return get(vector.x) === x && get(vector.y) === y; };