# Conic

#####

declaration
Conic

The conic implementation of MathLib makes calculations with conics possible.

    var Conic = (function () {
    		function Conic (primal, dual) {
    			this.type = 'conic';
    			if (primal.type !== 'matrix') {
    				primal = new MathLib.Matrix(primal);
    			}
    			this.primal = primal;

    			//		if (!dual) {
    			//			dual = primal.adjugate();
    			//		}
    			//		else if (!primal.times(dual).isScalar()) {
    			//			// Throw error
    			//		}
    			if (primal.rank() > 1) {
    				Object.defineProperties(this, {
    					'dual': {
    						get: function () {
    							return this.primal.adjugate();
    						},
    						set: function () {
    						},
    						enumerable: true,
    						configurable: true
    					}
    				});
    			}
    			else {
    				this.dual = dual;
    			}
    		}

# draw

#####

method
Conic.prototype.draw() → boolean

Option name | Type | Description
--- | --- | ---
screen | Screen | The screen to draw onto.
options | object | Drawing options
redraw | boolean | Indicates if the current draw call is happening during a redraw

Draws the conic on one or more screens

    Conic.prototype.draw = function (screen, options, redraw) {
    			if (typeof redraw === 'undefined') { redraw = false; }
    			if (Array.isArray(screen)) {
    				var conic = this;
    				screen.forEach(function (x) {
    					conic.draw(x, options);
    				});
    			}
    			else {
    				options.from = 0;
    				options.to = 2 * Math.PI;
    				options.conic = this;

    				var lines, alpha, cos, sin, sgn, a = this.primal[0][0], b = this.primal[0][1] * 2, c = this.primal[1][1], d = this.primal[0][2] * 2, e = this.primal[1][2] * 2, disc = 4 * a * c - b * b, rank = this.primal.rank(), cx = (b * e - 2 * c * d) / (4 * a * c - b * b), cy = (b * d - 2 * a * e) / (4 * a * c - b * b), normalForm = this.normalize(), A = Math.sqrt(Math.abs(normalForm.primal[2][2] / normalForm.primal[0][0])), C = Math.sqrt(Math.abs(normalForm.primal[2][2] / normalForm.primal[1][1]));

    				if (rank === 3) {
    					alpha = Math.atan2(b, a - c) / 2;
    					cos = Math.cos(alpha);
    					sin = Math.sin(alpha);

    					// Parabola
    					if (disc === 0) {
    						options.from = -10;
    						options.to = 10;

    						var param = -this.primal[1][2] / (2 * this.primal[0][0]);
    						cx = 0;
    						cy = this.primal[2][2] / this.primal[0][0];

    						screen.path([
    							function (t) {
    								return cx + cos * param * t * t - sin * 2 * param * t;
    							},
    							function (t) {
    								return cy + sin * param * t * t + cos * 2 * param * t;
    							}
    						], options, redraw);
    					}
    					else if (disc > 0) {
    						options.from = 0;
    						options.to = 2 * Math.PI;

    						screen.path([
    							function (t) {
    								return cx + cos * Math.cos(t) * A - sin * Math.sin(t) * C;
    							},
    							function (t) {
    								return cy + sin * Math.cos(t) * A + cos * Math.sin(t) * C;
    							}
    						], options, redraw);
    					}
    					else if (disc < 0) {
    						options.from = 0;
    						options.to = 2 * Math.PI;

    						// This function changes the direction of the path for the second branch.
    						// Otherwise we get some lines which shouldn't be there.
    						sgn = function (t) {
    							return +((t + Math.PI / 2) % (2 * Math.PI) < Math.PI) * 2 - 1;
    						};

    						if (normalForm.primal[2][2] * normalForm.primal[0][0] > 0) {
    							var swap = A;
    							A = C;
    							C = swap;

    							cos = Math.cos(alpha + Math.PI / 2);
    							sin = Math.sin(alpha + Math.PI / 2);
    						}
    						else {
    							cos = Math.cos(alpha);
    							sin = Math.sin(alpha);
    						}

    						screen.path([
    							function (t) {
    								return cx + cos * MathLib.sec(t) * A - sin * MathLib.tan(t) * C * sgn(t);
    							},
    							function (t) {
    								return cy + sin * MathLib.sec(t) * A + cos * MathLib.tan(t) * C * sgn(t);
    							}
    						], options, redraw);
    					}
    				}
    				else if (rank === 2) {
    					lines = this.splitDegenerated();

    					screen.line(lines[0], options);
    					screen.line(lines[1], options);
    				}
    				else if (rank === 1) {
    					lines = this.splitDegenerated();

    					screen.line(lines[0], options);
    				}
    			}
    			return this;
    		};

# eccentricity

#####

method
Conic.prototype.eccentricity() → number

Calculates the eccentricity of a conic.

    Conic.prototype.eccentricity = function () {
    			var normalform = this.normalize(), a = normalform.primal[0][0], c = normalform.primal[1][1];

    			if (!this.isDegenerated()) {
    				// parabola
    				if (c === 0) {
    					return 1;
    				}
    				if (c > 0) {
    					return Math.sqrt(1 - c / a);
    				}
    				return Math.sqrt(1 - a / c);
    			}
    		};

# isDegenerated

#####

method
Conic.prototype.isDegenerated() → boolean

Determines if a conic is degenerated.

    Conic.prototype.isDegenerated = function () {
    			return this.primal.rank() !== 3;
    		};

# isEqual

#####

method
Conic.prototype.isEqual() → boolean

Option name | Type | Description
--- | --- | ---
conic | Conic | The conic to be compared

Determines if two conics are equal.

    Conic.prototype.isEqual = function (conic) {
    			if (this === conic) {
    				return true;
    			}

    			var compare = function (M, N) {
    				var i, j, m, n;

    				if (M === N) {
    					return true;
    				}

    				nonZeroSearch:
    				for (i = 0; i < 3; i++) {
    					for (j = 0; j < 3; j++) {
    						if (M[i][j] !== 0) {
    							break nonZeroSearch;
    						}
    					}
    				}

    				if (N[i][j] === 0) {
    					return false;
    				}

    				m = M[i][j];
    				n = N[i][j];

    				for (i = 0; i < 3; i++) {
    					for (j = 0; j < 3; j++) {
    						if (n / m * M[i][j] !== N[i][j]) {
    							return false;
    						}
    					}
    				}

    				return true;
    			};

    			return compare(this.primal, conic.primal) && compare(this.dual, conic.dual);
    		};

# latusRectum

#####

method
Conic.prototype.latusRectum() → number

Calculates the latusRectum of a conic.

    Conic.prototype.latusRectum = function () {
    			var normalForm = this.normalize(), a = normalForm.primal[0][0], c = normalForm.primal[1][1], min = Math.min(Math.abs(a), Math.abs(c)), max = Math.max(Math.abs(a), Math.abs(c));

    			if (!this.isDegenerated()) {
    				// Parabola
    				if (c === 0) {
    					return -2 * normalForm.primal[1][2] / a;
    				}

    				return 2 * Math.sqrt(max) / min;
    			}
    		};

# linearEccentricity

#####

method
Conic.prototype.linearEccentricity() → number

Calculates the linear eccentricity of a conic.

    Conic.prototype.linearEccentricity = function () {
    			var normalForm = this.normalize(), a = normalForm.primal[0][0], c = normalForm.primal[1][1], max = Math.max(Math.abs(a), Math.abs(c)), min = Math.min(Math.abs(a), Math.abs(c));

    			if (!this.isDegenerated()) {
    				// parabola
    				if (c === 0) {
    					return normalForm.primal[1][2] / (-2 * a);
    				}

    				if (c > 0) {
    					return Math.sqrt(1 / min - 1 / max);
    				}
    				return Math.sqrt(1 / max + 1 / min);
    			}
    		};

# meet

#####

method
Conic.prototype.meet() → Point[]

Option name | Type | Description
--- | --- | ---
x | Line Conic | The line or conic to intersect with

Calculates the meet of the conic with a line or a conic.

    Conic.prototype.meet = function (x) {
    			var B, C, alpha, i, j, p1, p2, Ml, a, b, c, d, Delta0, Delta1, lambda, degenerated, lines, A = this.primal;

    			if (x.type === 'line') {
    				var setter = function () {
    					MathLib.warning({message: 'Trying to change the coordinates of a completely dependent point.', method: 'Conic#meet'});
    				}, recalculate = function () {
    					Ml = new MathLib.Matrix([[0, x[2], -x[1]], [-x[2], 0, x[0]], [x[1], -x[0], 0]]);
    					B = Ml.transpose().times(A).times(Ml);

    					if (!MathLib.isZero(x[0])) {
    						alpha = Math.sqrt(B[2][1] * B[1][2] - B[1][1] * B[2][2]) / x[0];
    					}
    					else if (!MathLib.isZero(x[1])) {
    						alpha = Math.sqrt(B[0][2] * B[2][0] - B[2][2] * B[0][0]) / x[1];
    					}
    					else {
    						alpha = Math.sqrt(B[1][0] * B[0][1] - B[0][0] * B[1][1]) / x[2];
    					}

    					C = Ml.times(alpha).plus(B);

    					nonZeroSearch:
    					for (i = 0; i < 3; i++) {
    						for (j = 0; j < 3; j++) {
    							if (C[i][j] !== 0) {
    								break nonZeroSearch;
    							}
    						}
    					}
    				};

    				recalculate();

    				p1 = new MathLib.Point(C[i]);
    				Object.defineProperties(p1, {
    					'0': {
    						get: function () {
    							recalculate();
    							return C[i][0];
    						},
    						set: setter,
    						enumerable: true
    					},
    					'1': {
    						get: function () {
    							recalculate();
    							return C[i][1];
    						},
    						set: setter,
    						enumerable: true
    					},
    					'2': {
    						get: function () {
    							recalculate();
    							return C[i][2];
    						},
    						set: setter,
    						enumerable: true
    					}
    				});

    				p2 = new MathLib.Point([C[0][j], C[1][j], C[2][j]]);
    				Object.defineProperties(p2, {
    					'0': {
    						get: function () {
    							recalculate();
    							return C[0][j];
    						},
    						set: setter,
    						enumerable: true
    					},
    					'1': {
    						get: function () {
    							recalculate();
    							return C[1][j];
    						},
    						set: setter,
    						enumerable: true
    					},
    					'2': {
    						get: function () {
    							recalculate();
    							return C[2][j];
    						},
    						set: setter,
    						enumerable: true
    					}
    				});

    				return [p1, p2];
    			}
    			else if (x.type === 'conic') {
    				B = x.primal;
    				a = A.determinant();
    				b = (new MathLib.Matrix([A[0], A[1], B[2]])).plus(new MathLib.Matrix([A[0], B[1], A[2]])).plus(new MathLib.Matrix([B[0], A[1], A[2]])).determinant();
    				c = (new MathLib.Matrix([A[0], B[1], B[2]])).plus(new MathLib.Matrix([B[0], A[1], B[2]])).plus(new MathLib.Matrix([B[0], B[1], A[2]])).determinant();
    				d = B.determinant();
    				Delta0 = b * b - 3 * a * c;
    				Delta1 = 2 * b * b - 9 * a * b * c + 27 * a * a * d;
    				C = MathLib.cbrt((Delta1 + Math.sqrt(Math.pow(Delta1, 2) - 4 * Math.pow(Delta0, 3))) / 2);
    				lambda = -(b + C + Delta0 / C) / (3 * a);
    				degenerated = new MathLib.Conic(B.times(lambda).plus(A));
    				lines = degenerated.splitDegenerated();

    				return this.meet(lines[0]).concat(this.meet(lines[1]));
    			}
    		};

# normalize

#####

method
Conic.prototype.normalize() → Conic

Calculates the normal form of a conic.

    Conic.prototype.normalize = function () {
    			var A = this.primal[0][0], B = this.primal[0][1] * 2, C = this.primal[1][1], D = this.primal[0][2] * 2, E = this.primal[1][2] * 2, F = this.primal[2][2], r = Math.atan2(B, A - C) / 2, cos = Math.cos(r), sin = Math.sin(r), a = A * cos * cos + B * sin * cos + C * sin * sin, c = A * sin * sin - B * sin * cos + C * cos * cos, d = D * cos + E * sin, e = E * cos - D * sin, f = F;

    			if (a !== 0) {
    				f += -d * d / (4 * a);
    				d = 0;
    			}

    			if (c !== 0) {
    				f += -e * e / (4 * c);
    				e = 0;
    			}

    			if (f !== 0) {
    				a = -a / f;
    				c = -c / f;
    				d = -d / f;
    				e = -e / f;
    				f = -1;
    			}

    			return new MathLib.Conic(new MathLib.Matrix([[a, 0, d / 2], [0, c, e / 2], [d / 2, e / 2, f]]));
    		};

# polarity

#####

method
Conic.prototype.polarity() → Point[]

Calculates the four polarity of a conic.

    Conic.prototype.polarity = function (x) {
    			var object, m, c = this;

    			if (x.type === 'line') {
    				object = new MathLib.Point([0, 0, 0]);
    				m = 'dual';
    			}
    			else if (x.type === 'point') {
    				object = new MathLib.Line([0, 0, 0]);
    				m = 'primal';
    			}

    			Object.defineProperties(object, {
    				'0': {
    					get: function () {
    						return c[m][0][0] * x[0] + c[m][0][1] * x[1] + c[m][0][2] * x[2];
    					},
    					set: function () {
    						MathLib.warning({message: 'Trying to change the coordinates of a completely dependent ' + object.type + '.', method: 'Conic#polarity'});
    					},
    					enumerable: true
    				},
    				'1': {
    					get: function () {
    						return c[m][1][0] * x[0] + c[m][1][1] * x[1] + c[m][1][2] * x[2];
    					},
    					set: function () {
    						MathLib.warning({message: 'Trying to change the coordinates of a completely dependent ' + object.type + '.', method: 'Conic#polarity'});
    					},
    					enumerable: true
    				},
    				'2': {
    					get: function () {
    						return c[m][2][0] * x[0] + c[m][2][1] * x[1] + c[m][2][2] * x[2];
    					},
    					set: function () {
    						MathLib.warning({message: 'Trying to change the coordinates of a completely dependent ' + object.type + '.', method: 'Conic#polarity'});
    					},
    					enumerable: true
    				}
    			});

    			return object;
    		};

# splitDegenerated

#####

method
Conic.prototype.splitDegenerated() → boolean

Splits a conic into one or two lines if the conic is degenerated.

    Conic.prototype.splitDegenerated = function () {
    			var n, i, j, B, C, p0, p1, p2, rank = this.primal.rank(), nonZeroSearch = function (C) {
    				for (i = 0; i < 3; i++) {
    					for (j = 0; j < 3; j++) {
    						if (C[i][j] !== 0) {
    							return;
    						}
    					}
    				}
    			};

    			if (rank === 2) {
    				if (this.dual[0][0] !== 0) {
    					n = 0;
    				}
    				else if (this.dual[1][1] !== 0) {
    					n = 1;
    				}
    				else {
    					n = 2;
    				}

    				if (this.dual[n][n] < 0) {
    					B = this.dual.negative();
    				}
    				else {
    					B = this.dual;
    				}

    				p0 = B[0][n] / Math.sqrt(B[n][n]);
    				p1 = B[1][n] / Math.sqrt(B[n][n]);
    				p2 = B[2][n] / Math.sqrt(B[n][n]);
    				C = this.primal.plus(new MathLib.Matrix([[0, p2, -p1], [-p2, 0, p0], [p1, -p0, 0]]));

    				nonZeroSearch(C);

    				return [new MathLib.Line(C[i]), new MathLib.Line([C[0][j], C[1][j], C[2][j]])];
    			}
    			else if (rank === 1) {
    				nonZeroSearch(this.primal);
    				return [new MathLib.Line(this.primal[i]), new MathLib.Line(this.primal[i])];
    			}
    		};

# throughFivePoints

#####

method
Conic.throughFivePoints() → Conic

Option name | Type | Description
--- | --- | ---
p | Point | The first point
q | Point | The second point
r | Point | The third point
s | Point | The fourth point
t | Point | The fifth point

Calculates the conic through five points.

    Conic.throughFivePoints = function (p, q, r, s, t) {
    			var conic = new MathLib.Conic(new MathLib.Matrix([[1, 0, 0], [0, 1, 0], [0, 0, 1]]));

    			Object.defineProperties(conic, {
    				'primal': {
    					get: function () {
    						var G = p.vectorProduct(r).outerProduct(q.vectorProduct(s)), H = p.vectorProduct(s).outerProduct(q.vectorProduct(r)), M = G.times(t.times(H).scalarProduct(t)).minus(H.times(t.times(G).scalarProduct(t)));
    						return M.transpose().plus(M);
    					},
    					set: function () {
    					},
    					enumerable: true,
    					configurable: true
    				}
    			});

    			return conic;
    		};
    		return Conic;
    	})();
    	MathLib.Conic = Conic;
    })(MathLib || (MathLib = {}));