blob: 5ede643c346e7845e5d3a802f740441191154858 [file] [log] [blame]
Simon Hunt195cb382014-11-03 17:50:51 -08001/*
2 * Copyright 2014 Open Networking Laboratory
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/*
18 Geometry library - based on work by Mike Bostock.
19 */
20
21(function() {
22
23 if (typeof geo == 'undefined') {
24 geo = {};
25 }
26
27 var tolerance = 1e-10;
28
29 function eq(a, b) {
30 return (Math.abs(a - b) < tolerance);
31 }
32
33 function gt(a, b) {
34 return (a - b > -tolerance);
35 }
36
37 function lt(a, b) {
38 return gt(b, a);
39 }
40
41 geo.eq = eq;
42 geo.gt = gt;
43 geo.lt = lt;
44
45 geo.LineSegment = function(x1, y1, x2, y2) {
46 this.x1 = x1;
47 this.y1 = y1;
48 this.x2 = x2;
49 this.y2 = y2;
50
51 // Ax + By = C
52 this.a = y2 - y1;
53 this.b = x1 - x2;
54 this.c = x1 * this.a + y1 * this.b;
55
56 if (eq(this.a, 0) && eq(this.b, 0)) {
57 throw new Error(
58 'Cannot construct a LineSegment with two equal endpoints.');
59 }
60 };
61
62 geo.LineSegment.prototype.intersect = function(that) {
63 var d = (this.x1 - this.x2) * (that.y1 - that.y2) -
64 (this.y1 - this.y2) * (that.x1 - that.x2);
65
66 if (eq(d, 0)) {
67 // The two lines are parallel or very close.
68 return {
69 x : NaN,
70 y : NaN
71 };
72 }
73
74 var t1 = this.x1 * this.y2 - this.y1 * this.x2,
75 t2 = that.x1 * that.y2 - that.y1 * that.x2,
76 x = (t1 * (that.x1 - that.x2) - t2 * (this.x1 - this.x2)) / d,
77 y = (t1 * (that.y1 - that.y2) - t2 * (this.y1 - this.y2)) / d,
78 in1 = (gt(x, Math.min(this.x1, this.x2)) && lt(x, Math.max(this.x1, this.x2)) &&
79 gt(y, Math.min(this.y1, this.y2)) && lt(y, Math.max(this.y1, this.y2))),
80 in2 = (gt(x, Math.min(that.x1, that.x2)) && lt(x, Math.max(that.x1, that.x2)) &&
81 gt(y, Math.min(that.y1, that.y2)) && lt(y, Math.max(that.y1, that.y2)));
82
83 return {
84 x : x,
85 y : y,
86 in1 : in1,
87 in2 : in2
88 };
89 };
90
91 geo.LineSegment.prototype.x = function(y) {
92 // x = (C - By) / a;
93 if (this.a) {
94 return (this.c - this.b * y) / this.a;
95 } else {
96 // a == 0 -> horizontal line
97 return NaN;
98 }
99 };
100
101 geo.LineSegment.prototype.y = function(x) {
102 // y = (C - Ax) / b;
103 if (this.b) {
104 return (this.c - this.a * x) / this.b;
105 } else {
106 // b == 0 -> vertical line
107 return NaN;
108 }
109 };
110
111 geo.LineSegment.prototype.length = function() {
112 return Math.sqrt(
113 (this.y2 - this.y1) * (this.y2 - this.y1) +
114 (this.x2 - this.x1) * (this.x2 - this.x1));
115 };
116
117 geo.LineSegment.prototype.offset = function(x, y) {
118 return new geo.LineSegment(
119 this.x1 + x, this.y1 + y,
120 this.x2 + x, this.y2 + y);
121 };
122
123})();