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