import Animated, { useSharedValue } from 'react-native-reanimated'; type SharedValueType = number; export type Vector<T extends SharedValueType> = { x: T; y: T; }; export type SharedVector<T extends SharedValueType> = { x: Animated.SharedValue<T>; y: Animated.SharedValue<T>; }; type VectorType = Vector<any> | SharedVector<any>; type VectorList = (VectorType | SharedValueType)[]; const _isVector = (value: any): value is Vector<any> => { 'worklet'; return typeof value.x !== 'undefined' && value.y !== 'undefined'; }; const isSharedValue = ( value: any, ): value is Animated.SharedValue<any> => { 'worklet'; return typeof value.value !== 'undefined'; }; const _get = < T extends Animated.SharedValue<SharedValueType> | SharedValueType, >( value: T, ) => { 'worklet'; if (isSharedValue(value)) { return value.value; } return value; }; type Operation = 'divide' | 'add' | 'sub' | 'multiply'; type VectorProp = 'x' | 'y'; const _reduce = ( operation: Operation, prop: VectorProp, vectors: VectorList, ) => { 'worklet'; const first = vectors[0]; const rest = vectors.slice(1); let res = _get(_isVector(first) ? first[prop] : first); for (let i = 0; i < rest.length; i++) { const current = rest[i]; const value = _get(_isVector(current) ? current[prop] : current); switch (operation) { case 'divide': if (value === 0) { res = 0; } else { res /= value; } break; case 'add': res += value; break; case 'sub': res -= value; break; case 'multiply': res *= value; break; default: break; } } return res; }; export const useSharedVector = <T>(x: T, y = x) => { return { x: useSharedValue(x), y: useSharedValue(y), }; }; export const create = <T extends SharedValueType>(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 = <T extends Vector<any>>(vector: T) => { 'worklet'; return { x: _get(vector.x) * -1, y: _get(vector.y) * -1, }; }; type Callback = () => any; export const set = <T extends VectorType>( 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.value = x; vector.y.value = y; } else { vector.x = x; vector.y = y; } }; export const min = (...vectors: VectorList) => { 'worklet'; const getMin = (prop: VectorProp) => { return Math.min.apply( void 0, 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( void 0, vectors.map((item) => _get(_isVector(item) ? item[prop] : item), ), ); return { x: getMax('x'), y: getMax('y'), }; }; export const clamp = <T extends Vector<any>>( value: T, lowerBound: VectorType | SharedValueType, upperBound: VectorType | SharedValueType, ) => { 'worklet'; return min(max(lowerBound, value), upperBound); }; export const eq = <T extends Vector<any>>( 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; };