309 lines
6.4 KiB
JavaScript
309 lines
6.4 KiB
JavaScript
|
// http://www.russellcottrell.com/fractalsEtc/cx.js
|
||
|
|
||
|
class cx {
|
||
|
static degrees(d) {
|
||
|
cx._RD = d ? Math.PI / 180 : 1;
|
||
|
}
|
||
|
// Math.PI/180 for degrees, 1 for radians
|
||
|
// applies to i/o (constructor, get/set arg, and toString etc.)
|
||
|
|
||
|
constructor(x, y, polar) {
|
||
|
if (!polar) {
|
||
|
this.re = x;
|
||
|
this.im = y;
|
||
|
} else {
|
||
|
y *= cx._RD; // may be radians or degrees
|
||
|
this.re = x * Math.cos(y);
|
||
|
this.im = x * Math.sin(y);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
get abs() {
|
||
|
return Math.sqrt(this.re * this.re + this.im * this.im);
|
||
|
}
|
||
|
|
||
|
set abs(r) {
|
||
|
var theta = this._arg;
|
||
|
this.re = r * Math.cos(theta);
|
||
|
this.im = r * Math.sin(theta);
|
||
|
}
|
||
|
|
||
|
get arg() {
|
||
|
// returns radians or degrees, non-negative
|
||
|
return (
|
||
|
((Math.atan2(this.im, this.re) + 2 * Math.PI) % (2 * Math.PI)) / cx._RD
|
||
|
);
|
||
|
}
|
||
|
|
||
|
set arg(theta) {
|
||
|
// may be radians or degrees
|
||
|
var r = this.abs;
|
||
|
this.re = r * Math.cos(theta * cx._RD);
|
||
|
this.im = r * Math.sin(theta * cx._RD);
|
||
|
}
|
||
|
|
||
|
get _arg() {
|
||
|
// internal; returns radians
|
||
|
return Math.atan2(this.im, this.re);
|
||
|
}
|
||
|
|
||
|
static get i() {
|
||
|
return new cx(0, 1);
|
||
|
}
|
||
|
|
||
|
static set i(x) {
|
||
|
throw new Error("i is read-only");
|
||
|
}
|
||
|
|
||
|
toString(polar) {
|
||
|
if (!polar)
|
||
|
return (
|
||
|
this.re.toString() +
|
||
|
(this.im >= 0 ? " + " : " - ") +
|
||
|
Math.abs(this.im).toString() +
|
||
|
"i"
|
||
|
);
|
||
|
else return this.abs.toString() + " cis " + this.arg.toString();
|
||
|
}
|
||
|
|
||
|
toPrecision(n, polar) {
|
||
|
if (!polar)
|
||
|
return (
|
||
|
this.re.toPrecision(n) +
|
||
|
(this.im >= 0 ? " + " : " - ") +
|
||
|
Math.abs(this.im).toPrecision(n) +
|
||
|
"i"
|
||
|
);
|
||
|
else return this.abs.toPrecision(n) + " cis " + this.arg.toPrecision(n);
|
||
|
}
|
||
|
|
||
|
toPrecis(n, polar) {
|
||
|
// trims trailing zeros
|
||
|
if (!polar)
|
||
|
return (
|
||
|
parseFloat(this.re.toPrecision(n)).toString() +
|
||
|
(this.im >= 0 ? " + " : " - ") +
|
||
|
parseFloat(Math.abs(this.im).toPrecision(n)).toString() +
|
||
|
"i"
|
||
|
);
|
||
|
else
|
||
|
return (
|
||
|
parseFloat(this.abs.toPrecision(n)).toString() +
|
||
|
" cis " +
|
||
|
parseFloat(this.arg.toPrecision(n)).toString()
|
||
|
);
|
||
|
}
|
||
|
|
||
|
toFixed(n, polar) {
|
||
|
if (!polar)
|
||
|
return (
|
||
|
this.re.toFixed(n) +
|
||
|
(this.im >= 0 ? " + " : " - ") +
|
||
|
Math.abs(this.im).toFixed(n) +
|
||
|
"i"
|
||
|
);
|
||
|
else return this.abs.toFixed(n) + " cis " + this.arg.toFixed(n);
|
||
|
}
|
||
|
|
||
|
toExponential(n, polar) {
|
||
|
if (!polar)
|
||
|
return (
|
||
|
this.re.toExponential(n) +
|
||
|
(this.im >= 0 ? " + " : " - ") +
|
||
|
Math.abs(this.im).toExponential(n) +
|
||
|
"i"
|
||
|
);
|
||
|
else return this.abs.toExponential(n) + " cis " + this.arg.toExponential(n);
|
||
|
}
|
||
|
|
||
|
static getReals(c, d) {
|
||
|
// when c or d may be simple or complex
|
||
|
var x, y, u, v;
|
||
|
if (c instanceof cx) {
|
||
|
x = c.re;
|
||
|
y = c.im;
|
||
|
} else {
|
||
|
x = c;
|
||
|
y = 0;
|
||
|
}
|
||
|
if (d instanceof cx) {
|
||
|
u = d.re;
|
||
|
v = d.im;
|
||
|
} else {
|
||
|
u = d;
|
||
|
v = 0;
|
||
|
}
|
||
|
return [x, y, u, v];
|
||
|
}
|
||
|
|
||
|
static conj(c) {
|
||
|
return new cx(c.re, -c.im);
|
||
|
}
|
||
|
|
||
|
static neg(c) {
|
||
|
return new cx(-c.re, -c.im);
|
||
|
}
|
||
|
|
||
|
static add(c, d) {
|
||
|
var a = cx.getReals(c, d);
|
||
|
var x = a[0];
|
||
|
var y = a[1];
|
||
|
var u = a[2];
|
||
|
var v = a[3];
|
||
|
return new cx(x + u, y + v);
|
||
|
}
|
||
|
|
||
|
static sub(c, d) {
|
||
|
var a = cx.getReals(c, d);
|
||
|
var x = a[0];
|
||
|
var y = a[1];
|
||
|
var u = a[2];
|
||
|
var v = a[3];
|
||
|
return new cx(x - u, y - v);
|
||
|
}
|
||
|
|
||
|
static mult(c, d) {
|
||
|
var a = cx.getReals(c, d);
|
||
|
var x = a[0];
|
||
|
var y = a[1];
|
||
|
var u = a[2];
|
||
|
var v = a[3];
|
||
|
return new cx(x * u - y * v, x * v + y * u);
|
||
|
}
|
||
|
|
||
|
static div(c, d) {
|
||
|
var a = cx.getReals(c, d);
|
||
|
var x = a[0];
|
||
|
var y = a[1];
|
||
|
var u = a[2];
|
||
|
var v = a[3];
|
||
|
return new cx(
|
||
|
(x * u + y * v) / (u * u + v * v),
|
||
|
(y * u - x * v) / (u * u + v * v)
|
||
|
);
|
||
|
}
|
||
|
|
||
|
static pow(c, int) {
|
||
|
if (Number.isInteger(int) && int >= 0) {
|
||
|
var r = Math.pow(c.abs, int);
|
||
|
var theta = int * c._arg;
|
||
|
return new cx(r * Math.cos(theta), r * Math.sin(theta));
|
||
|
} else return NaN;
|
||
|
}
|
||
|
|
||
|
static root(c, int, k) {
|
||
|
if (!k) k = 0;
|
||
|
if (
|
||
|
Number.isInteger(int) &&
|
||
|
int >= 2 &&
|
||
|
Number.isInteger(k) &&
|
||
|
k >= 0 &&
|
||
|
k < int
|
||
|
) {
|
||
|
var r = Math.pow(c.abs, 1 / int);
|
||
|
var theta = (c._arg + 2 * k * Math.PI) / int;
|
||
|
return new cx(r * Math.cos(theta), r * Math.sin(theta));
|
||
|
} else return NaN;
|
||
|
}
|
||
|
|
||
|
static log(c) {
|
||
|
return new cx(Math.log(c.abs), c._arg);
|
||
|
}
|
||
|
|
||
|
static exp(c) {
|
||
|
var r = Math.exp(c.re);
|
||
|
var theta = c.im;
|
||
|
return new cx(r * Math.cos(theta), r * Math.sin(theta));
|
||
|
}
|
||
|
|
||
|
static sin(c) {
|
||
|
var a = c.re;
|
||
|
var b = c.im;
|
||
|
return new cx(Math.sin(a) * Math.cosh(b), Math.cos(a) * Math.sinh(b));
|
||
|
}
|
||
|
|
||
|
static cos(c) {
|
||
|
var a = c.re;
|
||
|
var b = c.im;
|
||
|
return new cx(Math.cos(a) * Math.cosh(b), -Math.sin(a) * Math.sinh(b));
|
||
|
}
|
||
|
|
||
|
static tan(c) {
|
||
|
return cx.div(cx.sin(c), cx.cos(c));
|
||
|
}
|
||
|
|
||
|
static asin(c, k) {
|
||
|
if (!k) k = 0;
|
||
|
var ic = cx.mult(cx.i, c);
|
||
|
var c2 = cx.pow(c, 2);
|
||
|
return cx.mult(
|
||
|
cx.neg(cx.i),
|
||
|
cx.log(cx.add(ic, cx.root(cx.sub(1, c2), 2, k)))
|
||
|
);
|
||
|
}
|
||
|
|
||
|
static acos(c, k) {
|
||
|
if (!k) k = 0;
|
||
|
var c2 = cx.pow(c, 2);
|
||
|
return cx.mult(
|
||
|
cx.neg(cx.i),
|
||
|
cx.log(cx.add(c, cx.mult(cx.i, cx.root(cx.sub(1, c2), 2, k))))
|
||
|
);
|
||
|
}
|
||
|
|
||
|
static atan(c) {
|
||
|
return cx.mult(
|
||
|
cx.div(cx.i, 2),
|
||
|
cx.log(cx.div(cx.add(cx.i, c), cx.sub(cx.i, c)))
|
||
|
);
|
||
|
}
|
||
|
|
||
|
static sinh(c) {
|
||
|
var a = c.re;
|
||
|
var b = c.im;
|
||
|
return new cx(Math.sinh(a) * Math.cos(b), Math.cosh(a) * Math.sin(b));
|
||
|
}
|
||
|
|
||
|
static cosh(c) {
|
||
|
var a = c.re;
|
||
|
var b = c.im;
|
||
|
return new cx(Math.cosh(a) * Math.cos(b), Math.sinh(a) * Math.sin(b));
|
||
|
}
|
||
|
|
||
|
static tanh(c) {
|
||
|
return cx.div(cx.sinh(c), cx.cosh(c));
|
||
|
}
|
||
|
|
||
|
static asinh(c, k) {
|
||
|
if (!k) k = 0;
|
||
|
var c2 = cx.pow(c, 2);
|
||
|
return cx.log(cx.add(c, cx.root(cx.add(c2, 1), 2, k)));
|
||
|
}
|
||
|
|
||
|
static acosh(c, k) {
|
||
|
if (!k) k = 0;
|
||
|
var c2 = cx.pow(c, 2);
|
||
|
return cx.log(cx.add(c, cx.root(cx.sub(c2, 1), 2, k)));
|
||
|
}
|
||
|
|
||
|
static atanh(c) {
|
||
|
return cx.mult(cx.div(1, 2), cx.log(cx.div(cx.add(1, c), cx.sub(1, c))));
|
||
|
}
|
||
|
|
||
|
static copy(c) {
|
||
|
return new cx(c.re, c.im);
|
||
|
}
|
||
|
|
||
|
static eq(c, d, epsilon) {
|
||
|
if (!epsilon) {
|
||
|
if (c.re == d.re && c.im == d.im) return true;
|
||
|
} else {
|
||
|
if (Math.abs(c.re - d.re) < epsilon && Math.abs(c.im - d.im) < epsilon)
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
cx.degrees(true); // need to call this
|