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.
104 lines
2.7 KiB
104 lines
2.7 KiB
'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;
|
|
|