@tubular/math
Version:
Miscellaneous math functions
266 lines • 9.56 kB
JavaScript
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