@dominicstop/utils
Version:
Yet another event emitter written in typescript.
171 lines (138 loc) • 4.16 kB
text/typescript
export function lerp(
valueStart: number,
valueEnd: number,
percent: number
): number {
let deltaRange = valueEnd - valueStart;
let totalChange = deltaRange * percent;
let interpolatedValue = valueStart + totalChange;
return interpolatedValue;
};
/**
* solve for `percent`, given: `valueStart`, `valueEnd`, `interpolatedValue`
*
* lerp formula, solve for `p`
* ```
* iv = vs + p * (vs - ve)
* iv - vs = p * (vs - ve)
* (iv - vs) / (vs - ve) = p
* ```
*/
export function inverseLerp(
valueStart: number,
valueEnd: number,
interpolatedValue: number
): number {
const deltaChange = interpolatedValue - valueStart;
const deltaRange = valueEnd - valueStart;
const changePercent = deltaChange / deltaRange;
return changePercent;
};
export function rangedLerpUsingInputValue(
inputValue: number,
inputValueStart: number,
inputValueEnd: number,
outputValueStart: number,
outputValueEnd: number,
): number {
const inputValueAdj = inputValue - inputValueStart;
const inputRangeDelta = inputValueEnd - inputValueStart;
const progressRaw = inputValueAdj / inputRangeDelta;
const progress = Number.isFinite(progressRaw) ? progressRaw : 0;
return lerp(
/* valueStart: */ outputValueStart,
/* valueEnd : */ outputValueEnd,
/* percent : */ progress
);
};
export function rangedLerpUsingRelativePercent(
relativePercent: number,
inputValueStart: number,
inputValueEnd: number,
outputValueStart: number,
outputValueEnd: number
): number {
const rangeDelta = Math.abs(inputValueStart - inputValueEnd);
const inputValue = rangeDelta * relativePercent;
const percentRaw = inputValue / rangeDelta;
const percent = Number.isFinite(percentRaw) ? percentRaw : 0;
return lerp(
/* valueStart: */ outputValueStart,
/* valueEnd : */ outputValueEnd,
/* percent : */ percent
);
};
export function rangedLerpUsingArray(
inputValue: number,
rangeInput: Array<number>,
rangeOutput: Array<number>,
shouldClampMin: boolean = false,
shouldClampMax: boolean = false
): number | undefined {
if(
rangeInput.length != rangeOutput.length
|| rangeInput.length <= 2
){
return;
};
if(shouldClampMin && inputValue < rangeInput[0] ){
return rangeInput[0];
};
if(shouldClampMax && inputValue < rangeInput.slice(-1)[0]){
return rangeInput.pop();
};
// A - Extrapolate Left
if(inputValue < rangeInput[0]){
const rangeInputStart = rangeInput[0];
const rangeInputEnd = rangeInput[1];
const rangeOutputStart = rangeOutput[0];
const rangeOutputEnd = rangeOutput[1];
return rangedLerpUsingInputValue(
/* inputValue: */ inputValue,
/* inputValueStart: */ rangeInputEnd,
/* inputValueEnd: */ rangeInputStart,
/* outputValueStart: */ rangeOutputEnd,
/* outputValueEnd: */ rangeOutputStart
);
};
const [rangeStartIndex, rangeEndIndex] = (() => {
const matchIndex = rangeInput.findIndex((currentValue, index) => {
const nextValue = rangeInput[index + 1];
if(nextValue == null){
return false;
};
return inputValue >= currentValue && inputValue < nextValue;
});
// B - Interpolate Between
if(matchIndex != null){
return [matchIndex, matchIndex + 1];
};
const lastIndex = rangeInput.length - 1;
const secondToLastIndex = rangeInput.length - 2;
// C - Extrapolate Right
return [secondToLastIndex, lastIndex];
})();
const rangeInputStart = rangeInput[rangeStartIndex];
if(rangeInputStart == null){
return;
};
const rangeInputEnd = rangeInput[rangeEndIndex];
if(rangeInputEnd == null){
return;
};
const rangeOutputStart = rangeOutput[rangeStartIndex];
if(rangeOutputStart == null){
return;
};
const rangeOutputEnd = rangeOutput[rangeEndIndex];
if(rangeOutputEnd == null){
return;
};
return rangedLerpUsingInputValue(
/* inputValue : */ inputValue,
/* inputValueStart : */ rangeInputStart,
/* inputValueEnd : */ rangeInputEnd,
/* outputValueStart: */ rangeOutputStart,
/* outputValueEnd : */ rangeOutputEnd
);
};