// 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