// Copyright 2016 Christian d'Heureuse, Inventec Informatik AG, Zurich, Switzerland // www.source-code.biz, www.inventec.ch/chdh // // License: GPL, GNU General Public License, http://www.gnu.org/licenses/gpl.html // Home page: http://www.source-code.biz/snippets/typescript /// namespace Utils { import isInteger = Polyfills.isInteger; import sign = Polyfills.sign; export type UnivariateNumericFunction = (x: number) => number; export type BivariateNumericFunction = (x: number, y: number) => number; export class Point { public x: number; public y: number; constructor (x: number, y: number) { this.x = x; this.y = y; }} // Wraps the coordinate value r into the range 0..w. export function wrap (r: number, w: number) : number { // return (r + (w << 16)) % w; } // return (r % w + w) % w; } return (makeSmi(r) % makeSmi(w) + makeSmi(w)) % makeSmi(w); } // Forces a 31-bit integer number value to be stored as a SMI (small integer). // This is important for the optimization of the Google V8 JavaScript engine. export function makeSmi (i: number) : number { return i | 0; } // Returns true if the value is a 31-bit integer. export function isSmi (value: any) { return isInteger(value) && (value | 0) === value; } // Verifies that a value is a 31-bit integer. export function assertSmi (value: any) { if (!isSmi(value)) { throw new Error("Assertion error: not an SMI: " + value); }} // Same as Math.abs(), but must only used with 31-bit integers and therefore it // can be optimized and is faster with V8. export function absSmi (x: number) : number { return (x >= 0) ? x : -x; } // Normalizes an angle value to the value range -pi..+pi. export function normalizeAngle (x: number) : number { // Version 1 (only valid for input range -2pi..+2pi): /* if (x < -Math.PI) { return x + 2 * Math.PI; } if (x > Math.PI) { return x - 2 * Math.PI; } return x; */ // Version 2 (only valid for input range -2pi..+2pi): /* return (x + 3 * Math.PI) % (2 * Math.PI) - Math.PI; */ // Version 3: /* if (x < 0) { return (x - Math.PI) % (2 * Math.PI) + Math.PI; } else { return (x + Math.PI) % (2 * Math.PI) - Math.PI; } */ // Version 4: let s = sign(x); return (x + s * Math.PI) % (2 * Math.PI) - s * Math.PI; } //--- Enum --------------------------------------------------------------------- export interface Enum { // interface for TypeScript enum types [i: number]: string; // [s: string]: number; // not supported in TypeScript 2.0.3 enumCount: number; } // number of enum items, must be added as the last enum member // Loads the option elements for a HTML select element from an enum type. export function loadSelectElementOptionsEnum ($selectElement: JQuery, enumType: Enum, selected: number) { for (let i = 0; i < enumType.enumCount; i++) { let optionSelected: boolean = i == selected; let option: HTMLOptionElement = new Option(enumType[i], i.toString(), optionSelected, optionSelected); $selectElement.append($(option)); }} export function decodeEnum (enumType: Enum, memberName: string) : Number | null { let x: any = (enumType)[memberName]; return isInteger(x) ? x : null; } //--- Random ------------------------------------------------------------------- const frac32 = 2.3283064365386963e-10; // 2^-32 // This is an adaption of the Alea algorithm by Johannes Baagøe (http://baagoe.org) export class SeedableRandom { private s0: number; private s1: number; private s2: number; private c: number; constructor (seed: number = 0) { this.s0 = ((seed & 0x7FFFFFFF) ^ 0x384A5736) * frac32; this.s1 = ((seed & 0x7FFFFFFF) ^ 0x4FB835D3) * frac32; this.s2 = ((seed & 0x7FFFFFFF) ^ 0x7BC82631) * frac32; this.c = ((seed & 0x7FFFFFFF) ^ 0x2af7362b); } // Returns a random integer number within the range 0 .. i-1. public getInt (i: number) : number { return makeSmi(this.getUInt32() % i); } private getUInt32() : number { this.step(); return this.s2 * 0x100000000; } // 2^32 private step() { let t = 2091639 * this.s0 + this.c * frac32; this.s0 = this.s1; this.s1 = this.s2; this.c = t | 0; this.s2 = t - this.c; }} //--- BitSpace ----------------------------------------------------------------- // This class manages a two-dimensional matrix of bits (an array of boolean values). /* export class BitSpace { public readonly width: number; public readonly height: number; public readonly totalCells: number; // the total number of cells within the bitmap public setCells: number; // the number of cells that have been set to true private a: Uint8Array; constructor (width: number, height: number) { this.width = makeSmi(width); this.height = makeSmi(height); this.totalCells = this.width * this.height; this.setCells = 0; if (typeof Uint8Array !== "function") { throw new Error("Uint8Array class is not supported."); } this.a = new Uint8Array(Math.ceil(this.totalCells / 8)); } // Sets a cell to true. public setCell (x: number, y: number) { let p = y * this.width + x; let i = makeSmi(p / 8); let m = 1 << (p % 8); if (this.a[i] & m) { return; } // cell is already set this.a[i] |= m; this.setCells++; } // Returns the value of a cell. public getCell (x: number, y: number) : boolean { let p = y * this.width + x; let i = makeSmi(p / 8); let m = 1 << (p % 8); return (this.a[i] & m) != 0; }} */ // This class manages a two-dimensional matrix of bits (an array of boolean values). // Alternate implementation with bytes instead of bits. export class BitSpace { public readonly width: number; public readonly height: number; public readonly totalCells: number; // the total number of cells within the bitmap public setCells: number; // the number of cells that have been set to true private a: Uint8Array; constructor (width: number, height: number) { this.width = makeSmi(width); this.height = makeSmi(height); this.totalCells = this.width * this.height; this.setCells = 0; if (typeof Uint8Array !== "function") { throw new Error("Uint8Array class is not supported."); } this.a = new Uint8Array(this.totalCells); } // Sets a cell to true. public setCell (x: number, y: number) { let p = y * this.width + x; if (this.a[p]) { return; } // cell is already set this.a[p] = 1; this.setCells++; } // Returns the value of a cell. public getCell (x: number, y: number) : boolean { let p = y * this.width + x; return this.a[p] != 0; }} //--- Function value caching --------------------------------------------------- // Creates a value cache for a univariate numeric function and returns a new // function that uses linear interpolation to approximate a value that lies // between two cached values. export function createLinearInterpolationCacheFunction (f: UnivariateNumericFunction, xMin: number, xMax: number, cacheSize: number) : UnivariateNumericFunction { if (!isInteger(cacheSize) || cacheSize < 2 || xMax <= xMin) { throw new Error(); } let cache = new Float64Array(cacheSize); let step = (xMax - xMin) / (cacheSize - 1); for (let i = 0; i < cacheSize; i++) { cache[i] = f(xMin + i * step); } return function (x: number) { let p = (x - xMin) / step; let i = Math.floor(p); if (i <= 0) { return cache[0]; } if (i >= cacheSize - 1) { return cache[cacheSize - 1]; } let d = p - i; let x0 = cache[i]; let x1 = cache[i + 1]; return x0 + (x1 - x0) * d; }; } } // end namespace Utils