1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.geometry.core.partitioning.test;
18
19 import org.apache.commons.geometry.core.Transform;
20 import org.apache.commons.geometry.core.partitioning.EmbeddingHyperplane;
21 import org.apache.commons.geometry.core.partitioning.Hyperplane;
22 import org.apache.commons.geometry.core.partitioning.HyperplaneLocation;
23
24
25
26
27 public final class TestLine implements EmbeddingHyperplane<TestPoint2D, TestPoint1D> {
28
29
30 public static final TestLine X_AXIS = new TestLine(0, 0, 1, 0);
31
32
33 public static final TestLine Y_AXIS = new TestLine(0, 0, 0, 1);
34
35
36 private final double directionX;
37
38
39 private final double directionY;
40
41
42 private final double originOffset;
43
44
45
46
47
48
49 public TestLine(final TestPoint2D p1, final TestPoint2D p2) {
50 this(p1.getX(), p1.getY(), p2.getX(), p2.getY());
51 }
52
53
54
55
56
57
58
59 public TestLine(final double x1, final double y1, final double x2, final double y2) {
60 double vecX = x2 - x1;
61 double vecY = y2 - y1;
62
63 final double norm = norm(vecX, vecY);
64
65 vecX /= norm;
66 vecY /= norm;
67
68 if (!Double.isFinite(vecX) || !Double.isFinite(vecY)) {
69 throw new IllegalStateException("Unable to create line between points: (" +
70 x1 + ", " + y1 + "), (" + x2 + ", " + y2 + ")");
71 }
72
73 this.directionX = vecX;
74 this.directionY = vecY;
75
76 this.originOffset = signedArea(vecX, vecY, x1, y1);
77 }
78
79
80
81
82 public TestPoint2D getOrigin() {
83 return toSpace(0);
84 }
85
86
87
88
89 public double getDirectionX() {
90 return directionX;
91 }
92
93
94
95
96 public double getDirectionY() {
97 return directionY;
98 }
99
100
101 @Override
102 public double offset(final TestPoint2D point) {
103 return originOffset - signedArea(directionX, directionY, point.getX(), point.getY());
104 }
105
106
107 @Override
108 public HyperplaneLocation classify(final TestPoint2D point) {
109 final double offset = offset(point);
110 final double cmp = PartitionTestUtils.PRECISION.compare(offset, 0.0);
111 if (cmp == 0) {
112 return HyperplaneLocation.ON;
113 }
114 return cmp < 0 ? HyperplaneLocation.MINUS : HyperplaneLocation.PLUS;
115 }
116
117
118 @Override
119 public boolean contains(final TestPoint2D point) {
120 return classify(point) == HyperplaneLocation.ON;
121 }
122
123
124
125
126
127 public double toSubspaceValue(final TestPoint2D point) {
128 return (directionX * point.getX()) + (directionY * point.getY());
129 }
130
131
132 @Override
133 public TestPoint1D toSubspace(final TestPoint2D point) {
134 return new TestPoint1D(toSubspaceValue(point));
135 }
136
137
138
139
140
141 public TestPoint2D toSpace(final double abscissa) {
142 if (Double.isInfinite(abscissa)) {
143 final double dirXCmp = PartitionTestUtils.PRECISION.signum(directionX);
144 final double dirYCmp = PartitionTestUtils.PRECISION.signum(directionY);
145
146 final double x;
147 if (dirXCmp == 0) {
148
149 x = getOrigin().getX();
150 } else {
151 x = (dirXCmp < 0 ^ abscissa < 0) ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY;
152 }
153
154 final double y;
155 if (dirYCmp == 0) {
156
157 y = getOrigin().getY();
158 } else {
159 y = (dirYCmp < 0 ^ abscissa < 0) ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY;
160 }
161
162 return new TestPoint2D(x, y);
163 }
164
165 final double ptX = (abscissa * directionX) + (-originOffset * directionY);
166 final double ptY = (abscissa * directionY) + (originOffset * directionX);
167
168 return new TestPoint2D(ptX, ptY);
169 }
170
171
172 @Override
173 public TestPoint2D toSpace(final TestPoint1D point) {
174 return toSpace(point.getX());
175 }
176
177
178 @Override
179 public TestPoint2D project(final TestPoint2D point) {
180 return toSpace(toSubspaceValue(point));
181 }
182
183
184 @Override
185 public TestLine reverse() {
186 final TestPoint2D pt = getOrigin();
187 return new TestLine(pt.getX(), pt.getY(), pt.getX() - directionX, pt.getY() - directionY);
188 }
189
190
191 @Override
192 public TestLine transform(final Transform<TestPoint2D> transform) {
193 final TestPoint2D p1 = transform.apply(toSpace(0));
194 final TestPoint2D p2 = transform.apply(toSpace(1));
195
196 return new TestLine(p1, p2);
197 }
198
199
200 @Override
201 public boolean similarOrientation(final Hyperplane<TestPoint2D> other) {
202 final TestLine otherLine = (TestLine) other;
203 final double dot = (directionX * otherLine.directionX) + (directionY * otherLine.directionY);
204 return dot >= 0.0;
205 }
206
207
208 @Override
209 public TestLineSegment span() {
210 return new TestLineSegment(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, this);
211 }
212
213
214
215
216
217
218
219 public TestPoint2D intersection(final TestLine other) {
220 final double area = signedArea(directionX, directionY, other.directionX, other.directionY);
221 if (PartitionTestUtils.PRECISION.eqZero(area)) {
222
223 return null;
224 }
225
226 final double x = ((other.directionX * originOffset) +
227 (-directionX * other.originOffset)) / area;
228
229 final double y = ((other.directionY * originOffset) +
230 (-directionY * other.originOffset)) / area;
231
232 return new TestPoint2D(x, y);
233 }
234
235
236 @Override
237 public String toString() {
238 final StringBuilder sb = new StringBuilder();
239 sb.append(this.getClass().getSimpleName())
240 .append("[origin= ")
241 .append(getOrigin())
242 .append(", direction= (")
243 .append(directionX)
244 .append(", ")
245 .append(directionY)
246 .append(")]");
247
248 return sb.toString();
249 }
250
251
252
253
254
255
256
257
258
259
260 private static double signedArea(final double x1, final double y1,
261 final double x2, final double y2) {
262 return (x1 * y2) + (-y1 * x2);
263 }
264
265
266
267
268
269
270 public static double norm(final double x, final double y) {
271 return Math.sqrt((x * x) + (y * y));
272 }
273 }