@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
279 lines (257 loc) • 7.69 kB
text/typescript
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;
}