UNPKG

@fdx/fxmath

Version:

A helper library for vector math and generative art

294 lines (236 loc) 7.5 kB
import {V2,v2} from './v2'; declare interface FxHashAPI { hash: string; rand: () => number; minter: string; randminter: () => number; preview: () => void; isPreview: boolean; params: (definitions: any) => void; getParam: (id: string) => any; getParams: () => any; getRawParam: (id: string) => any; getRawParams: () => any; getDefinitions: () => any; features: (features: any) => void; getFeature: (id: string) => any; getFeatures: () => any; stringifyParams: (definitions: any) => string; } declare interface Window { $fx: FxHashAPI; } export type IPos = { x: number; y: number; } const {PI,log,sin,cos,tan,atan,atan2,random,floor,ceil,sqrt,round, exp,pow} = Math; const PI2 = PI * 2; const GOLDENRATIO = 1.61803398875; const makeFibonacci = (n:number) => { let arr = [0,1]; for(let i = 2; i < n; i++){ arr.push(arr[i-1] + arr[i-2]); } return arr; } const map = (n: number, start: number, stop: number, targetStart: number, targetStop: number) => { const newVal = targetStart + (n - start) / (stop - start) * (targetStop - targetStart); return newVal; } const lerp = (start: number, stop: number, amt: number) => { return amt * (stop - start) + start; } const mix = lerp; const dist = (ax: number, ay: number, bx: number, by: number) => { return Math.sqrt(Math.pow((ax - bx), 2) + Math.pow((ay - by), 2)); } const rnd = (a: number, b: number,mathrandom = false) => { const random = mathrandom ? Math.random() :RND() return a + (b - a) * random; } const rndInt = (low:number,high:number) => { return low + Math.floor( RND() * ( high - low + 1 ) ); } const random2 = (a: number,seed = 12345) => { let x = Math.sin(a * seed) * 10000; return x - Math.floor(x); } // https://stackoverflow.com/questions/521295/seeding-the-random-number-generator-in-javascript const mulberry32RND = (a:number) => { return function() { var t = a += 0x6D2B79F5; t = Math.imul(t ^ t >>> 15, t | 1); t ^= t + Math.imul(t ^ t >>> 7, t | 61); return ((t ^ t >>> 14) >>> 0) / 4294967296; } } // @ts-ignore if(window?.$fx){console.log("fxhash-api detected",window?.$fx.hash,window?.$fx.rand())} // @ts-ignore let mullBerryRND = mulberry32RND((typeof window !== "undefined" && window?.$fx) ? $fx.rand()*10000 : Math.random()*10000) const resetRNDHASH = (hash:number) => { mullBerryRND = mulberry32RND(hash) } const RND = () => { // @ts-ignore return mullBerryRND(); //Math.random(); } const weightedRandomLn = (rand0to1: number, zeroHasHeigherProb = true) => { let e = Math.exp(1); let x0 = Math.pow(e, 2) - 1; // At x0 this will become 1 --- let x = x0 * rand0to1; let rnd = (Math.log(1 + x) / 2); if(zeroHasHeigherProb){ return 1 - rnd; } else{ return rnd; } } const rand_box_muller = () : number => { let u = 0, v = 0; while(u === 0) u = RND(); while(v === 0) v = RND(); let num = Math.sqrt( -2.0 * Math.log( u ) ) * Math.cos( 2.0 * Math.PI * v ); num = num / 10.0 + 0.5; // Translate to 0 -> 1 if (num > 1 || num < 0) return rand_box_muller() // resample between 0 and 1 return num } const pickRandom = (...args: any[]) => { let len = args.length; let rnd = Math.round(RND() * (len - 1)); return args[rnd]; } const pickRandomFromArray = <T>(arr: T[],splice=false) => { let len = arr.length; // @ts-ignore let _rnd = window.fxrand ? fxrand() : Math.random(); let rnd = Math.round(_rnd * (len - 1)); if(splice){ let el = arr.splice(rnd,1); return el[0]; } return arr[rnd]; } const fract = (x: number) => { return x - Math.floor(x); } const clamp = (x: number, min = 0, max = 1) => { return Math.min(max, Math.max(x, min)); } const modWrap = (x: number, min = 0, max = 1) => { return (x+(max-min)) % (max - min) + min; } const smoothstep = (edge0: number, edge1: number, x: number) => { const t = clamp((x - edge0) / (edge1 - edge0), 0, 1); return t * t * (3 - 2 * t); } const quinticinterpol = (edge0: number, edge1: number, x0: number) => { const x = clamp((x0 - edge0) / (edge1 - edge0), 0, 1); return x * x * x * (x * (x * 6 - 15) + 10); } const createPseudoPoissonDistribution = (OPT: {W: number, H: number, size: number, perc: number, hasShiftRow: boolean}) => { const {W,H,size,perc,hasShiftRow} = OPT; let dots: V2[][] = []; let anzX = Math.ceil(W / size); let anzY = Math.ceil(H / size); for(let i = 0; i < anzY; i++){ let row:V2[] = []; for(let j = 0; j < anzX; j++){ let shiftrow = hasShiftRow ? (i % 2 === 0 ? size / 2 : 0) : 0; let _rnd = RND(); let _rnd2 = RND(); let phi = _rnd * 2 * Math.PI; let dr = weightedRandomLn(_rnd2) * perc / 100 * size; let dx = dr * Math.cos(phi); let dy = dr * Math.sin(phi); row.push(V2.create( j * size + size / 2 + shiftrow + dx, i * size + size / 2 + dy )) } dots.push(row) } return dots; } const randomWeightedFromArray = <T>(arr:{value:T,prob:number,start?: number, end?: number}[]) => { let probSum = arr.reduce((prev, curr) => { return prev + curr.prob }, 0); let iterate = 0; arr.forEach(item => { item.start = iterate; item.end = iterate + item.prob; iterate = item.end; }) // @ts-ignore let r = window.fxrand ? fxrand() : Math.random(); let rnd = r * probSum; let theOne = arr.find((itm: any) => { return (itm.start <= rnd && itm.end > rnd); }) return theOne!.value as T } const swapVals = (a:number,b:number) => { let temp = b; b = a; a = temp; return [] } const isEven = (n: number) => { return n % 2 === 0; } function sawTooth(_x:number, A:number) { let x = _x + 1000*A; var result = x % (2 * A); if(result > A) { return 2 * A - result; } else { return result; } } function make2dSquareArray(MAP_DIMENSION: number) { let map3d: any[][] = []; for (let iy = 0; iy < MAP_DIMENSION; iy++) { let row: number[] = []; for (let ix = 0; ix < MAP_DIMENSION; ix++) { row.push(0); } map3d.push(row); } return map3d; } function make2dArray(MAP_DIMENSION_Y: number,MAP_DIMENSION_X: number) { let map3d: any[][] = []; for (let iy = 0; iy < MAP_DIMENSION_Y; iy++) { let row: number[] = []; for (let ix = 0; ix < MAP_DIMENSION_X; ix++) { row.push(0); } map3d.push(row); } return map3d; } function shuffleArray(array:any[]) { let currentIndex = array.length, randomIndex; // While there remain elements to shuffle... while (currentIndex !== 0) { // Pick a remaining element... randomIndex = Math.floor(RND() * currentIndex); currentIndex--; // And swap it with the current element. [array[currentIndex], array[randomIndex]] = [ array[randomIndex], array[currentIndex]]; } return array; } function debounce<T extends (...args: any[]) => void>(func: T, delay: number): T { let timeoutId: ReturnType<typeof setTimeout> | null = null; return ((...args: Parameters<T>) => { if (timeoutId) clearTimeout(timeoutId); timeoutId = setTimeout(() => func(...args), delay); }) as T; } export {resetRNDHASH, map, lerp, mix, dist, rnd,RND, random2, fract, clamp, smoothstep,quinticinterpol, weightedRandomLn, pickRandomFromArray, pickRandom, createPseudoPoissonDistribution,randomWeightedFromArray, PI,PI2,log,sin,cos,tan,atan2,atan,random,floor,ceil,sqrt,swapVals,round,exp,pow,rndInt,isEven,rand_box_muller, sawTooth,make2dArray,make2dSquareArray,modWrap,shuffleArray,makeFibonacci,debounce }