mattermost-mobile/app/utils/gallery/vectors.ts
2025-07-22 14:00:57 +08:00

238 lines
5 KiB
TypeScript

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import Animated, {useSharedValue, type SharedValue} from 'react-native-reanimated';
type SharedValueType = number;
type Callback = () => unknown;
type VectorType = Vector<any> | SharedVector<any>;
type VectorList = Array<VectorType | SharedValueType>;
type Operation = 'divide' | 'add' | 'sub' | 'multiply';
type VectorProp = 'x' | 'y';
export type Vector<T extends SharedValueType> = {
x: T;
y: T;
};
export type SharedVector<T extends SharedValueType> = {
x: Animated.SharedValue<T>;
y: Animated.SharedValue<T>;
};
const isVector = (value: any): value is Vector<any> => {
'worklet';
return 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 SharedValue<SharedValueType> | 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 type ShareVectorType<T> = {
x: SharedValue<T>;
y: SharedValue<T>;
}
export const useSharedVector = <T>(x: T, y = x): ShareVectorType<T> => {
return {
x: useSharedValue(x),
y: useSharedValue(y),
};
};
export const create = <T extends SharedValueType>(x: T, y: T): Vector<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,
};
};
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();
return;
}
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 = <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;
};