AffineFunction = {
let AffineFunction = class AffineFunction {
constructor(Ab) {
if (AffineFunction.is_affine_list(Ab)) {
this.f = _Ab_to_function(Ab);
this.affine_list = Ab;
this.linear_part = Ab[0];
this.shift = Ab[1];
this.is_similarity = _is_similarity(Ab);
this.norm = _norm(Ab);
this.is_contractive = this.norm < 1;
this.fixed_point = _fixed_point(Ab);
} else {
return Ab;
}
}
};
function _Ab_to_function(Ab) {
let A = Ab[0];
let b = Ab[1] || [0, 0];
let f = function (xy) {
return [
A[0][0] * xy[0] + A[0][1] * xy[1] + b[0],
A[1][0] * xy[0] + A[1][1] * xy[1] + b[1]
];
};
return f;
}
function _fixed_point(Ab) {
let A = Ab[0];
let a = A[0][0];
let b = A[0][1];
let c = A[1][0];
let d = A[1][1];
let [e, f] = Ab[1];
if (a * d - b * c != 0) {
let denominator = 1 - a - b * c - d + a * d;
return [
(e - d * e + b * f) / denominator,
(c * e + f - a * f) / denominator
];
} else {
return null;
}
}
function _norm(Ab) {
let A = Ab[0];
let a = A[0][0];
let b = A[0][1];
let c = A[1][0];
let d = A[1][1];
return (
Math.sqrt(
Math.pow(a, 2) +
Math.pow(b, 2) +
Math.pow(c, 2) +
Math.pow(d, 2) +
Math.sqrt(
(Math.pow(b + c, 2) + Math.pow(a - d, 2)) *
(Math.pow(b - c, 2) + Math.pow(a + d, 2))
)
) / Math.sqrt(2)
);
}
function _is_similarity(Ab) {
let eps = 0.00000001;
let a, b, c, d;
[[a, b], [c, d]] = Ab[0];
return (
Math.abs(a * a + c * c - (b * b + d * d)) < eps &&
Math.abs(a * c + b * d) < eps
);
}
// Checks to make sure we've got good input
AffineFunction.is_numeric = function (x) {
return !isNaN(parseFloat(x)) && isFinite(x);
};
AffineFunction.is_vector = function (b) {
return (
Array.isArray(b) &&
b.length == 2 &&
AffineFunction.is_numeric(b[0]) &&
AffineFunction.is_numeric(b[1])
);
};
AffineFunction.is_matrix = function (A) {
return (
Array.isArray(A) &&
A.length == 2 &&
Array.isArray(A[0]) &&
A[0].length == 2 &&
Array.isArray(A[1]) &&
A[1].length == 2 &&
AffineFunction.is_numeric(A[0][0]) &&
AffineFunction.is_numeric(A[0][1]) &&
AffineFunction.is_numeric(A[1][0]) &&
AffineFunction.is_numeric(A[1][1])
);
};
AffineFunction.is_affine_list = function (Ab) {
return (
Array.isArray(Ab) &&
Ab.length == 2 &&
AffineFunction.is_matrix(Ab[0]) &&
AffineFunction.is_vector(Ab[1])
);
};
AffineFunction.prototype.compose = function (Ms) {
let M, s;
if (Ms instanceof AffineFunction) {
M = Ms.linear_part;
s = Ms.shift;
} else {
M = Ms[0];
s = Ms[1];
}
let m11, m12, m21, m22, s1, s2;
m11 = M[0][0];
m12 = M[0][1];
m21 = M[1][0];
m22 = M[1][1];
s1 = s[0];
s2 = s[1];
let A, a11, a12, a21, a22, b1, b2;
A = this.linear_part;
a11 = A[0][0];
a12 = A[0][1];
a21 = A[1][0];
a22 = A[1][1];
b1 = this.shift[0];
b2 = this.shift[1];
let af = new AffineFunction([
[
[a11 * m11 + a12 * m21, a11 * m12 + a12 * m22],
[a21 * m11 + a22 * m21, a21 * m12 + a22 * m22]
],
[b1 + a11 * s1 + a12 * s2, b2 + a21 * s1 + a22 * s2]
]);
return af;
};
AffineFunction.prototype.invert = function () {
let A = this.linear_part;
let a = A[0][0];
let b = A[0][1];
let c = A[1][0];
let d = A[1][1];
let [e, f] = this.shift;
let det = a * d - b * c;
if (det != 0) {
let B = [
[d / det, -b / det],
[-c / det, a / det]
];
let s = [(b * f - d * e) / det, (c * e - a * f) / det];
return new AffineFunction([B, s]);
}
};
AffineFunction.prototype.equal = function (af) {
let eps = 10 ** -3;
let A = this.linear_part;
let a1 = A[0][0];
let b1 = A[0][1];
let c1 = A[1][0];
let d1 = A[1][1];
let [e1, f1] = this.shift;
let B = af.linear_part;
let a2 = B[0][0];
let b2 = B[0][1];
let c2 = B[1][0];
let d2 = B[1][1];
let [e2, f2] = af.shift;
//return [a1 - a2, b1 - b2, c1 - c2, d1 - d2, e1 - e2, f1 - f2];
return (
Math.abs(a1 - a2) < eps &&
Math.abs(b1 - b2) < eps &&
Math.abs(c1 - c2) < eps &&
Math.abs(d1 - d2) < eps &&
Math.abs(e1 - e2) < eps &&
Math.abs(f1 - f2) < eps
);
};
AffineFunction.scale = function (r, x0y0) {
let x0, y0;
if (x0y0) {
x0 = x0y0[0];
y0 = x0y0[1];
} else {
x0 = 0;
y0 = 0;
}
let af = new AffineFunction([
[
[r, 0],
[0, r]
],
[x0 - r * x0, y0 - r * y0]
]);
return af;
};
AffineFunction.shift = function (direction) {
let af = new AffineFunction([
[
[1, 0],
[0, 1]
],
direction
]);
return af;
};
AffineFunction.rotate = function (theta, x0y0) {
let x0, y0;
if (x0y0) {
x0 = x0y0[0];
y0 = x0y0[1];
} else {
x0 = 0;
y0 = 0;
}
let A = [
[Math.cos(theta), -Math.sin(theta)],
[Math.sin(theta), Math.cos(theta)]
];
let b = [
x0 * (1 - Math.cos(theta)) + y0 * Math.sin(theta),
y0 * (1 - Math.cos(theta)) - x0 * Math.sin(theta)
];
let af = new AffineFunction([A, b]);
return af;
};
AffineFunction.reflect = function (direction, x0y0) {
let a = direction[0];
let b = direction[1];
let norm_squared = a * a + b * b;
let A = [
[1 - (2 * a * a) / norm_squared, (-2 * a * b) / norm_squared],
[(-2 * a * b) / norm_squared, 1 - (2 * b * b) / norm_squared]
];
let x0, y0;
if (x0y0) {
x0 = x0y0[0];
y0 = x0y0[1];
} else {
x0 = 0;
y0 = 0;
}
b = [
(2 * a * (a * x0 + b * y0)) / norm_squared,
(2 * b * (a * x0 + b * y0)) / norm_squared
];
let af = new AffineFunction([A, b]);
return af;
};
return AffineFunction;
}