(function() { if (typeof geo == 'undefined') { geo = {}; } var tolerance = 1e-10; function eq(a, b) { return (Math.abs(a - b) < tolerance); } function gt(a, b) { return (a - b > -tolerance); } function lt(a, b) { return gt(b, a); } geo.eq = eq; geo.gt = gt; geo.lt = lt; geo.LineSegment = function(x1, y1, x2, y2) { this.x1 = x1; this.y1 = y1; this.x2 = x2; this.y2 = y2; // Ax + By = C this.a = y2 - y1; this.b = x1 - x2; this.c = x1 * this.a + y1 * this.b; if (eq(this.a, 0) && eq(this.b, 0)) { throw new Error( 'Cannot construct a LineSegment with two equal endpoints.'); } }; geo.LineSegment.prototype.intersect = function(that) { var d = (this.x1 - this.x2) * (that.y1 - that.y2) - (this.y1 - this.y2) * (that.x1 - that.x2); if (eq(d, 0)) { // The two lines are parallel or very close. return { x : NaN, y : NaN }; } var t1 = this.x1 * this.y2 - this.y1 * this.x2, t2 = that.x1 * that.y2 - that.y1 * that.x2, x = (t1 * (that.x1 - that.x2) - t2 * (this.x1 - this.x2)) / d, y = (t1 * (that.y1 - that.y2) - t2 * (this.y1 - this.y2)) / d, in1 = (gt(x, Math.min(this.x1, this.x2)) && lt(x, Math.max(this.x1, this.x2)) && gt(y, Math.min(this.y1, this.y2)) && lt(y, Math.max(this.y1, this.y2))), in2 = (gt(x, Math.min(that.x1, that.x2)) && lt(x, Math.max(that.x1, that.x2)) && gt(y, Math.min(that.y1, that.y2)) && lt(y, Math.max(that.y1, that.y2))); return { x : x, y : y, in1 : in1, in2 : in2 }; }; geo.LineSegment.prototype.x = function(y) { // x = (C - By) / a; if (this.a) { return (this.c - this.b * y) / this.a; } else { // a == 0 -> horizontal line return NaN; } }; geo.LineSegment.prototype.y = function(x) { // y = (C - Ax) / b; if (this.b) { return (this.c - this.a * x) / this.b; } else { // b == 0 -> vertical line return NaN; } }; geo.LineSegment.prototype.length = function() { return Math.sqrt( (this.y2 - this.y1) * (this.y2 - this.y1) + (this.x2 - this.x1) * (this.x2 - this.x1)); }; geo.LineSegment.prototype.offset = function(x, y) { return new geo.LineSegment( this.x1 + x, this.y1 + y, this.x2 + x, this.y2 + y); }; })();