You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
105 lines
2.7 KiB
105 lines
2.7 KiB
2 years ago
|
'use strict';
|
||
|
|
||
|
/* eslint-disable space-infix-ops */
|
||
|
|
||
|
// The precision used to consider an ellipse as a circle
|
||
|
//
|
||
|
var epsilon = 0.0000000001;
|
||
|
|
||
|
// To convert degree in radians
|
||
|
//
|
||
|
var torad = Math.PI / 180;
|
||
|
|
||
|
// Class constructor :
|
||
|
// an ellipse centred at 0 with radii rx,ry and x - axis - angle ax.
|
||
|
//
|
||
|
function Ellipse(rx, ry, ax) {
|
||
|
if (!(this instanceof Ellipse)) { return new Ellipse(rx, ry, ax); }
|
||
|
this.rx = rx;
|
||
|
this.ry = ry;
|
||
|
this.ax = ax;
|
||
|
}
|
||
|
|
||
|
// Apply a linear transform m to the ellipse
|
||
|
// m is an array representing a matrix :
|
||
|
// - -
|
||
|
// | m[0] m[2] |
|
||
|
// | m[1] m[3] |
|
||
|
// - -
|
||
|
//
|
||
|
Ellipse.prototype.transform = function (m) {
|
||
|
// We consider the current ellipse as image of the unit circle
|
||
|
// by first scale(rx,ry) and then rotate(ax) ...
|
||
|
// So we apply ma = m x rotate(ax) x scale(rx,ry) to the unit circle.
|
||
|
var c = Math.cos(this.ax * torad), s = Math.sin(this.ax * torad);
|
||
|
var ma = [
|
||
|
this.rx * (m[0]*c + m[2]*s),
|
||
|
this.rx * (m[1]*c + m[3]*s),
|
||
|
this.ry * (-m[0]*s + m[2]*c),
|
||
|
this.ry * (-m[1]*s + m[3]*c)
|
||
|
];
|
||
|
|
||
|
// ma * transpose(ma) = [ J L ]
|
||
|
// [ L K ]
|
||
|
// L is calculated later (if the image is not a circle)
|
||
|
var J = ma[0]*ma[0] + ma[2]*ma[2],
|
||
|
K = ma[1]*ma[1] + ma[3]*ma[3];
|
||
|
|
||
|
// the discriminant of the characteristic polynomial of ma * transpose(ma)
|
||
|
var D = ((ma[0]-ma[3])*(ma[0]-ma[3]) + (ma[2]+ma[1])*(ma[2]+ma[1])) *
|
||
|
((ma[0]+ma[3])*(ma[0]+ma[3]) + (ma[2]-ma[1])*(ma[2]-ma[1]));
|
||
|
|
||
|
// the "mean eigenvalue"
|
||
|
var JK = (J + K) / 2;
|
||
|
|
||
|
// check if the image is (almost) a circle
|
||
|
if (D < epsilon * JK) {
|
||
|
// if it is
|
||
|
this.rx = this.ry = Math.sqrt(JK);
|
||
|
this.ax = 0;
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
// if it is not a circle
|
||
|
var L = ma[0]*ma[1] + ma[2]*ma[3];
|
||
|
|
||
|
D = Math.sqrt(D);
|
||
|
|
||
|
// {l1,l2} = the two eigen values of ma * transpose(ma)
|
||
|
var l1 = JK + D/2,
|
||
|
l2 = JK - D/2;
|
||
|
// the x - axis - rotation angle is the argument of the l1 - eigenvector
|
||
|
/*eslint-disable indent*/
|
||
|
this.ax = (Math.abs(L) < epsilon && Math.abs(l1 - K) < epsilon) ?
|
||
|
90
|
||
|
:
|
||
|
Math.atan(Math.abs(L) > Math.abs(l1 - K) ?
|
||
|
(l1 - J) / L
|
||
|
:
|
||
|
L / (l1 - K)
|
||
|
) * 180 / Math.PI;
|
||
|
/*eslint-enable indent*/
|
||
|
|
||
|
// if ax > 0 => rx = sqrt(l1), ry = sqrt(l2), else exchange axes and ax += 90
|
||
|
if (this.ax >= 0) {
|
||
|
// if ax in [0,90]
|
||
|
this.rx = Math.sqrt(l1);
|
||
|
this.ry = Math.sqrt(l2);
|
||
|
} else {
|
||
|
// if ax in ]-90,0[ => exchange axes
|
||
|
this.ax += 90;
|
||
|
this.rx = Math.sqrt(l2);
|
||
|
this.ry = Math.sqrt(l1);
|
||
|
}
|
||
|
|
||
|
return this;
|
||
|
};
|
||
|
|
||
|
// Check if the ellipse is (almost) degenerate, i.e. rx = 0 or ry = 0
|
||
|
//
|
||
|
Ellipse.prototype.isDegenerate = function () {
|
||
|
return (this.rx < epsilon * this.ry || this.ry < epsilon * this.rx);
|
||
|
};
|
||
|
|
||
|
module.exports = Ellipse;
|