UNPKG

@polygonjs/polygonjs

Version:

node-based WebGL 3D engine https://polygonjs.com

279 lines (257 loc) 7.69 kB
import type {TypedArray} from 'three'; import {pushOnArrayAtEntry} from './MapUtils'; import {setUnion, setToArray, setIntersection, setDifference, setXOR} from './SetUtils'; import {isString} from './Type'; import {randFloat} from './math/_Module'; export type ArrayToItemFunction<T> = (array: Array<T>) => T; const _tmp: Set<any> = new Set(); const _tmp0: Set<any> = new Set(); const _tmp1: Set<any> = new Set(); export function range(start: number, end: number, step: number, target: number[]): number[] { if (end == null) { end = start; start = 0; } const length = Math.floor((end - start) / step); target.length = length; for (let i = 0; i < length; i++) { target[i] = start + i * step; } return target; } export function rangeWithEnd(end: number): number[] { const target: number[] = []; range(0, end, 1, target); return target; } export function rangeStartEnd(start: number, end: number): number[] { const target: number[] = []; range(start, end, 1, target); return target; } export function arrayUniq<T>(array: Array<T>, target: Array<T>): Array<T> { target.length = 0; for (const element of array) { if (!target.includes(element)) { target.push(element); } } return target; // if we use a set, we lose the order // const tmpSet: Set<T> = new Set(); // for (let elem of array) { // tmpSet.add(elem); // } // const newArray: Array<T> = new Array(tmpSet.size); // let i = 0; // tmpSet.forEach((elem) => { // newArray[i] = elem; // i++; // }); // return newArray; } export function sampleIndex(array: Array<any>, seed: number): number { return Math.floor(randFloat(seed) * array.length); } export function sample<T>(array: Array<T>, seed: number): T | undefined { return array[sampleIndex(array, seed)]; } export function spliceSample<T>(array: Array<T>, seed: number): T | undefined { return array.splice(sampleIndex(array, seed), 1)[0]; } export function uniqWithoutPreservingOrder<T>(array: Array<T>, target: Array<T>): Array<T> { arrayToSet(array, _tmp); return setToArray(_tmp, target); // for (let elem of array) { // tmpSet.add(elem); // } // const newArray: Array<T> = new Array(tmpSet.size); // let i = 0; // tmpSet.forEach((elem) => { // newArray[i] = elem; // i++; // }); // return newArray; } export function arrayCompact<T>(array: Readonly<Array<T | null | undefined>>, target: Array<T>): Array<T> { target.length = 0; for (let i = 0; i < array.length; i++) { const elem = array[i]; if (elem != null) { target.push(elem); } } return target; } export function arrayMin<T>(array: Array<T>): T { let min = array[0]; for (let i = 0; i < array.length; i++) { if (array[i] < min) { min = array[i]; } } return min; } export function arrayMax<T>(array: Array<T>): T { let max = array[0]; for (let i = 0; i < array.length; i++) { if (array[i] > max) { max = array[i]; } } return max; } export function arraySum(array: number[]): number { let sum = 0; for (let i = 0; i < array.length; i++) { sum += array[i]; } return sum; } export function arrayChunk<T>(array: Array<T>, chunkSize: number): Array<Array<T>> { const newArray: Array<Array<T>> = []; let newSubArray: Array<T> = []; newArray.push(newSubArray); for (let i = 0; i < array.length; i++) { if (newSubArray.length == chunkSize) { newSubArray = []; newArray.push(newSubArray); } newSubArray.push(array[i]); } return newArray; } export function arrayUnion<T>(array0: Array<T>, array1: Array<T>, target: Array<T>): Array<T> { setUnion(arrayToSet(array0, _tmp0), arrayToSet(array1, _tmp1), _tmp); setToArray(_tmp, target); return target; } export function arrayIntersection<T>(array0: Array<T>, array1: Array<T>, target: Array<T>): Array<T> { setIntersection(arrayToSet(array0, _tmp0), arrayToSet(array1, _tmp1), _tmp); setToArray(_tmp, target); return target; } export function arrayDifference<T>(array0: Array<T>, array1: Array<T>, target: Array<T>): Array<T> { setDifference(arrayToSet(array0, _tmp0), arrayToSet(array1, _tmp1), _tmp); setToArray(_tmp, target); return target; } export function arrayXOR<T>(array0: Array<T>, array1: Array<T>, target: Array<T>): Array<T> { setXOR(arrayToSet(array0, _tmp0), arrayToSet(array1, _tmp1), _tmp); setToArray(_tmp, target); return target; } export function arrayToSet<T>(array: Array<T>, target: Set<T>): Set<T> { target.clear(); for (let i = 0; i < array.length; i++) { target.add(array[i]); } return target; } export function arrayIsEqual<T extends number | string>(array0: Array<T>, array1: Array<T>): boolean { if (array0.length != array1.length) { return false; } const count = array0.length; for (let i = 0; i < count; i++) { if (array0[i] != array1[i]) { return false; } } return true; } export function arraySortBy<T, K extends number | string>(array: Array<T>, callback: (e: T) => K): Array<T> { if (array.length == 0) { return []; } const elementsByValue: Map<K, T[]> = new Map(); const valuesSet: Set<K> = new Set(); for (const elem of array) { const value: K = callback(elem); valuesSet.add(value); pushOnArrayAtEntry(elementsByValue, value, elem); } const values: K[] = new Array(valuesSet.size); let i = 0; valuesSet.forEach((value) => { values[i] = value; i++; }); // sort differently if the callback return value is a string or a number if (isString(values[0])) { values.sort(); } else { values.sort((a, b) => (a as number) - (b as number)); } const sorted_elements: Array<T> = new Array(array.length); i = 0; for (const value of values) { const elements_for_value = elementsByValue.get(value); if (elements_for_value) { for (const element of elements_for_value) { sorted_elements[i] = element; i++; } } } return sorted_elements; } export function arrayShallowClone<T>(array: Array<T>): Array<T> { // https://stackoverflow.com/questions/3978492/fastest-way-to-duplicate-an-array-in-javascript-slice-vs-for-loop return [...array]; } export function arrayMap<T, U>(array: Readonly<Array<T>>, callback: (e: T) => U, target: Array<U>): Array<U> { target.length = 0; for (const item of array) { target.push(callback(item)); } return target; } export function arrayAverage(array: number[]) { return arraySum(array) / array.length; } export class ArrayUtils { static shallowClone = arrayShallowClone; static min = arrayMin; static max = arrayMax; static sum = arraySum; static compact = arrayCompact; static uniq = arrayUniq; static uniqWithoutPreservingOrder = uniqWithoutPreservingOrder; static chunk = arrayChunk; static union = arrayUnion; static intersection = arrayIntersection; static toSet = arrayToSet; static isEqual = arrayIsEqual; static sortBy = arraySortBy; static range = range; } // const MAX_ITEMS_LENGTH = 1024; export function arrayPushItems<T>(srcArray: Readonly<Array<T>>, target: Array<T>) { // we avoid the standard // array.push(...items), // as this can trigger an // 'Maximum call stack size exceeded' error // on some large items array. // So instead, we push them elements one by one if items.length is above a threshold // if (srcArray.length <= MAX_ITEMS_LENGTH) { // target.push(...srcArray); // } else { for (const item of srcArray) { target.push(item); } // } } export function arrayCopy<T>(srcArray: Readonly<Array<T> | ArrayLike<T>>, targetArray: Array<T>): void { targetArray.length = srcArray.length; for (let i = 0; i < srcArray.length; i++) { targetArray[i] = srcArray[i]; } } export function typedArrayCopy<T1 extends TypedArray, T2 extends TypedArray>( srcArray: Readonly<T1>, targetArray: T2 ): T2 { targetArray.set(srcArray); return targetArray; }