UNPKG

@tubular/math

Version:
266 lines 9.56 kB
import { abs, floor, mod, mod2, pow, round } from './math'; export var Unit; (function (Unit) { Unit[Unit["RADIANS"] = 0] = "RADIANS"; Unit[Unit["DEGREES"] = 1] = "DEGREES"; Unit[Unit["ARC_MINUTES"] = 2] = "ARC_MINUTES"; Unit[Unit["ARC_SECONDS"] = 3] = "ARC_SECONDS"; Unit[Unit["HOURS"] = 4] = "HOURS"; Unit[Unit["HOUR_ANGLE_MINUTES"] = 5] = "HOUR_ANGLE_MINUTES"; Unit[Unit["HOUR_ANGLE_SECONDS"] = 6] = "HOUR_ANGLE_SECONDS"; Unit[Unit["ROTATIONS"] = 7] = "ROTATIONS"; Unit[Unit["GRADS"] = 8] = "GRADS"; })(Unit || (Unit = {})); export var Mode; (function (Mode) { Mode[Mode["RANGE_LIMIT_SIGNED"] = 0] = "RANGE_LIMIT_SIGNED"; Mode[Mode["RANGE_LIMIT_NONNEGATIVE"] = 1] = "RANGE_LIMIT_NONNEGATIVE"; Mode[Mode["RANGE_UNLIMITED"] = 2] = "RANGE_UNLIMITED"; })(Mode || (Mode = {})); export const PI = Math.PI; export const HALF_PI = PI / 2.0; export const TWO_PI = PI * 2.0; export const FMT_DD = 0x01; export const FMT_HH = 0x01; export const FMT_DDD = 0x02; export const FMT_MINS = 0x04; export const FMT_SECS = 0x08; export const FMT_SIGNED = 0x10; export function convertToRadians(angle, unit) { switch (unit) { case Unit.RADIANS: return angle; case Unit.DEGREES: return angle / 180.0 * PI; case Unit.ARC_MINUTES: return angle / 10800.0 * PI; case Unit.ARC_SECONDS: return angle / 648000.0 * PI; case Unit.HOURS: return angle / 12.0 * PI; case Unit.HOUR_ANGLE_MINUTES: return angle / 720.0 * PI; case Unit.HOUR_ANGLE_SECONDS: return angle / 43200.0 * PI; case Unit.ROTATIONS: return angle * TWO_PI; case Unit.GRADS: return angle / 200.0 * PI; } return NaN; } export function convertFromRadians(angle, unit) { switch (unit) { case Unit.RADIANS: return angle; case Unit.DEGREES: return angle * 180.0 / PI; case Unit.ARC_MINUTES: return angle * 10800.0 / PI; case Unit.ARC_SECONDS: return angle * 648000.0 / PI; case Unit.HOURS: return angle * 12.0 / PI; case Unit.HOUR_ANGLE_MINUTES: return angle * 720.0 / PI; case Unit.HOUR_ANGLE_SECONDS: return angle * 43200.0 / PI; case Unit.ROTATIONS: return angle / TWO_PI; case Unit.GRADS: return angle * 200.0 / PI; } return NaN; } export class Angle { constructor(angle = 0, unit, mode = Mode.RANGE_LIMIT_SIGNED) { this.cached_sin = 2.0; this.cached_cos = 2.0; this.cached_tan = 0.0; if (unit === undefined) unit = Unit.RADIANS; if (mode === Mode.RANGE_LIMIT_SIGNED) this.angle = mod2(convertToRadians(angle, unit), TWO_PI); else if (mode === Mode.RANGE_LIMIT_NONNEGATIVE) this.angle = mod(convertToRadians(angle, unit), TWO_PI); else this.angle = convertToRadians(angle, unit); } static asin(x) { return new Angle(Math.asin(x)); } static asin_nonneg(x) { return new Angle(Math.asin(x), Unit.RADIANS, Mode.RANGE_LIMIT_NONNEGATIVE); } static acos(x) { return new Angle(Math.acos(x)); } static atan(x) { return new Angle(Math.atan(x)); } static atan_nonneg(x) { return new Angle(Math.atan(x), Unit.RADIANS, Mode.RANGE_LIMIT_NONNEGATIVE); } static atan2(y, x) { return new Angle(Math.atan2(y, x)); } static atan2_nonneg(y, x) { return new Angle(Math.atan2(y, x), Unit.RADIANS, Mode.RANGE_LIMIT_NONNEGATIVE); } get radians() { return this.angle; } get degrees() { return convertFromRadians(this.angle, Unit.DEGREES); } get arcMinutes() { return convertFromRadians(this.angle, Unit.ARC_MINUTES); } get arcSeconds() { return convertFromRadians(this.angle, Unit.ARC_SECONDS); } get hours() { return convertFromRadians(this.angle, Unit.HOURS); } get rotations() { return convertFromRadians(this.angle, Unit.ROTATIONS); } get grads() { return convertFromRadians(this.angle, Unit.GRADS); } getAngle(unit = Unit.RADIANS) { return convertFromRadians(this.angle, unit); } get sin() { if (this.cached_sin > 1.0) this.cached_sin = Math.sin(this.angle); return this.cached_sin; } get cos() { if (this.cached_cos > 1.0) this.cached_cos = Math.cos(this.angle); return this.cached_cos; } get tan() { if (this.angle === 0.0) return 0.0; else if (this.cached_tan === 0.0) this.cached_tan = Math.tan(this.angle); return this.cached_tan; } add(angle2, mode = Mode.RANGE_LIMIT_SIGNED) { return new Angle(this.angle + angle2.angle, Unit.RADIANS, mode); } add_nonneg(angle2) { return new Angle(this.angle + angle2.angle, Unit.RADIANS, Mode.RANGE_LIMIT_NONNEGATIVE); } subtract(angle2, mode = Mode.RANGE_LIMIT_SIGNED) { return new Angle(this.angle - angle2.angle, Unit.RADIANS, mode); } subtract_nonneg(angle2) { return new Angle(this.angle - angle2.angle, Unit.RADIANS, Mode.RANGE_LIMIT_NONNEGATIVE); } complement(mode = Mode.RANGE_LIMIT_SIGNED) { return new Angle(HALF_PI - this.angle, Unit.RADIANS, mode); } complement_nonneg() { return new Angle(HALF_PI - this.angle, Unit.RADIANS, Mode.RANGE_LIMIT_NONNEGATIVE); } supplement(mode = Mode.RANGE_LIMIT_SIGNED) { return new Angle(PI - this.angle, Unit.RADIANS, mode); } supplement_nonneg() { return new Angle(PI - this.angle, Unit.RADIANS, Mode.RANGE_LIMIT_NONNEGATIVE); } opposite(mode = Mode.RANGE_LIMIT_SIGNED) { return new Angle(this.angle + PI, Unit.RADIANS, mode); } opposite_nonneg() { return new Angle(this.angle + PI, Unit.RADIANS, Mode.RANGE_LIMIT_NONNEGATIVE); } negate(mode = Mode.RANGE_LIMIT_SIGNED) { return new Angle(-this.angle, Unit.RADIANS, mode === undefined ? Mode.RANGE_UNLIMITED : mode); } // Sounds contradictory, doesn't it? Return whatever angle is 180 degrees away, as a non-negative value. negate_nonneg() { return new Angle(-this.angle, Unit.RADIANS, Mode.RANGE_LIMIT_NONNEGATIVE); } multiply(x, mode = Mode.RANGE_LIMIT_SIGNED) { return new Angle(this.angle * x, Unit.RADIANS, mode); } multiply_nonneg(x) { return new Angle(this.angle * x, Unit.RADIANS, Mode.RANGE_LIMIT_NONNEGATIVE); } divide(x, mode = Mode.RANGE_LIMIT_SIGNED) { return new Angle(this.angle / x, Unit.RADIANS, mode); } divide_nonneg(x) { return new Angle(this.angle / x, Unit.RADIANS, Mode.RANGE_LIMIT_NONNEGATIVE); } toString(format, precision) { return Angle.toStringAux(this.degrees, '\u00B0', '\'', '"', format, precision); } toSuffixedString(positiveSuffix, negativeSuffix, format, precision) { return Angle.toStringAux(abs(this.degrees), '\u00B0', '\'', '"', format, precision) + (this.degrees < 0 ? negativeSuffix : positiveSuffix); } toHourString(format, precision) { return Angle.toStringAux(this.hours, 'h', 'm', 's', format, precision); } toTimeString(format, precision) { return Angle.toStringAux(this.hours, ':', format === FMT_MINS ? '' : ':', '', format, precision, 2); } static toStringAux(units, delim1, delim2, delim3, format, precision, unitsPadding = 0) { format = (format || 0); const sxg = ((format & (FMT_MINS | FMT_SECS)) !== 0); if ((format & FMT_DD) !== 0) unitsPadding = 2; else if ((format & FMT_DDD) !== 0) unitsPadding = 3; if (precision == null) { if (format != null && sxg) precision = 0; else precision = 3; } const sign = Math.sign(units); units = abs(units); let result; if (sxg) { const pwr = pow(10, precision); if ((format & FMT_MINS) !== 0) { let mins = round(units * 60 * pwr) / pwr; units = floor(mins / 60); mins = mins % 60; result = units + delim1 + (mins < 10 ? '0' : '') + mins.toFixed(precision) + delim2; } else { let secs = round(units * 3600 * pwr) / pwr; let mins = floor(secs / 60); secs = secs % 60; units = floor(mins / 60); mins = mins % 60; result = units + delim1 + (mins < 10 ? '0' : '') + mins + delim2 + (secs < 10 ? '0' : '') + secs.toFixed(precision) + delim3; } } else { result = units.toFixed(precision) + delim1; } if (unitsPadding) { const match = /^(\d+)\D/.exec(result); const padding = unitsPadding - match[1].length; for (let i = 0; i < padding; ++i) result = '0' + result; } if (sign < 0) result = '-' + result; else if ((format & FMT_SIGNED) !== 0) result = '+' + result; return result; } } Angle.ZERO = new Angle(0.0); Angle.RIGHT = new Angle(HALF_PI); Angle.STRAIGHT = new Angle(PI); //# sourceMappingURL=angle.js.map