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 java.util.Collections;
20 import java.util.List;
21
22 import org.apache.commons.geometry.core.RegionLocation;
23 import org.apache.commons.geometry.core.Transform;
24 import org.apache.commons.geometry.core.partitioning.Hyperplane;
25 import org.apache.commons.geometry.core.partitioning.HyperplaneConvexSubset;
26 import org.apache.commons.geometry.core.partitioning.Split;
27
28
29
30
31 public final class TestLineSegment implements HyperplaneConvexSubset<TestPoint2D> {
32
33 private final double start;
34
35
36 private final double end;
37
38
39 private final TestLine line;
40
41
42
43
44
45 public TestLineSegment(final TestPoint2D start, final TestPoint2D end) {
46 this.line = new TestLine(start, end);
47
48 final double startValue = line.toSubspaceValue(start);
49 final double endValue = line.toSubspaceValue(end);
50
51 this.start = Math.min(startValue, endValue);
52 this.end = Math.max(startValue, endValue);
53 }
54
55
56
57
58
59
60
61 public TestLineSegment(final double x1, final double y1, final double x2, final double y2) {
62 this(new TestPoint2D(x1, y1), new TestPoint2D(x2, y2));
63 }
64
65
66
67
68
69
70 public TestLineSegment(final double start, final double end, final TestLine line) {
71 this.start = Math.min(start, end);
72 this.end = Math.max(start, end);
73 this.line = line;
74 }
75
76
77
78
79 public double getStart() {
80 return start;
81 }
82
83
84
85
86 public double getEnd() {
87 return end;
88 }
89
90
91
92
93 public TestPoint2D getStartPoint() {
94 return line.toSpace(start);
95 }
96
97
98
99
100 public TestPoint2D getEndPoint() {
101 return line.toSpace(end);
102 }
103
104
105 @Override
106 public TestLine getHyperplane() {
107 return line;
108 }
109
110
111 @Override
112 public boolean isFull() {
113 return start < end && Double.isInfinite(start) && Double.isInfinite(end);
114
115 }
116
117
118 @Override
119 public boolean isEmpty() {
120 return PartitionTestUtils.PRECISION.eqZero(getSize());
121 }
122
123
124 @Override
125 public boolean isInfinite() {
126 return Double.isInfinite(getSize());
127 }
128
129
130 @Override
131 public boolean isFinite() {
132 return !isInfinite();
133 }
134
135
136 @Override
137 public double getSize() {
138 return Math.abs(start - end);
139 }
140
141
142 @Override
143 public TestPoint2D getCentroid() {
144 return line.toSpace(0.5 * (end - start));
145 }
146
147
148 @Override
149 public RegionLocation classify(final TestPoint2D point) {
150 if (line.contains(point)) {
151 final double value = line.toSubspaceValue(point);
152
153 final int startCmp = PartitionTestUtils.PRECISION.compare(value, start);
154 final int endCmp = PartitionTestUtils.PRECISION.compare(value, end);
155
156 if (startCmp == 0 || endCmp == 0) {
157 return RegionLocation.BOUNDARY;
158 } else if (startCmp > 0 && endCmp < 0) {
159 return RegionLocation.INSIDE;
160 }
161 }
162
163 return RegionLocation.OUTSIDE;
164 }
165
166
167 @Override
168 public TestPoint2D closest(final TestPoint2D point) {
169 double value = line.toSubspaceValue(point);
170 value = Math.max(Math.min(value, end), start);
171
172 return line.toSpace(value);
173 }
174
175
176 @Override
177 public List<HyperplaneConvexSubset<TestPoint2D>> toConvex() {
178 return Collections.singletonList(this);
179 }
180
181
182 @Override
183 public TestLineSegment reverse() {
184 final TestLine rLine = line.reverse();
185 return new TestLineSegment(-end, -start, rLine);
186 }
187
188
189 @Override
190 public Split<TestLineSegment> split(final Hyperplane<TestPoint2D> splitter) {
191 final TestLine splitterLine = (TestLine) splitter;
192
193 if (isInfinite()) {
194 return splitInfinite(splitterLine);
195 }
196 return splitFinite(splitterLine);
197 }
198
199
200 @Override
201 public HyperplaneConvexSubset<TestPoint2D> transform(final Transform<TestPoint2D> transform) {
202 if (!isInfinite()) {
203
204 final TestPoint2D p1 = transform.apply(getStartPoint());
205 final TestPoint2D p2 = transform.apply(getEndPoint());
206
207 return new TestLineSegment(p1, p2);
208 }
209
210
211 final TestPoint2D p0 = transform.apply(line.toSpace(0));
212 final TestPoint2D p1 = transform.apply(line.toSpace(1));
213
214 final TestLine tLine = new TestLine(p0, p1);
215 final double translation = tLine.toSubspaceValue(p0);
216 final double scale = tLine.toSubspaceValue(p1);
217
218 final double tStart = (start * scale) + translation;
219 final double tEnd = (end * scale) + translation;
220
221 return new TestLineSegment(tStart, tEnd, tLine);
222 }
223
224
225 @Override
226 public String toString() {
227 final StringBuilder sb = new StringBuilder();
228 sb.append(this.getClass().getSimpleName())
229 .append("[start= ")
230 .append(getStartPoint())
231 .append(", end= ")
232 .append(getEndPoint())
233 .append("]");
234
235 return sb.toString();
236 }
237
238
239
240
241
242
243 private Split<TestLineSegment> splitInfinite(final TestLine splitter) {
244 final TestPoint2D intersection = splitter.intersection(line);
245
246 if (intersection == null) {
247
248 final double originOffset = splitter.offset(line.getOrigin());
249
250 final double sign = PartitionTestUtils.PRECISION.signum(originOffset);
251 if (sign < 0) {
252 return new Split<>(this, null);
253 } else if (sign > 0) {
254 return new Split<>(null, this);
255 }
256 return new Split<>(null, null);
257 } else {
258
259 final double intersectionAbscissa = line.toSubspaceValue(intersection);
260
261 TestLineSegment startSegment = null;
262 TestLineSegment endSegment = null;
263
264 if (start < intersectionAbscissa) {
265 startSegment = new TestLineSegment(start, intersectionAbscissa, line);
266 }
267 if (intersectionAbscissa < end) {
268 endSegment = new TestLineSegment(intersectionAbscissa, end, line);
269 }
270
271 final double startOffset = splitter.offset(line.toSpace(intersectionAbscissa - 1));
272 final double startCmp = PartitionTestUtils.PRECISION.signum(startOffset);
273
274 final TestLineSegment minus = (startCmp > 0) ? endSegment : startSegment;
275 final TestLineSegment plus = (startCmp > 0) ? startSegment : endSegment;
276
277 return new Split<>(minus, plus);
278 }
279 }
280
281
282
283
284
285
286 private Split<TestLineSegment> splitFinite(final TestLine splitter) {
287
288 final double startOffset = splitter.offset(line.toSpace(start));
289 final double endOffset = splitter.offset(line.toSpace(end));
290
291 final double startCmp = PartitionTestUtils.PRECISION.signum(startOffset);
292 final double endCmp = PartitionTestUtils.PRECISION.signum(endOffset);
293
294
295
296
297
298
299
300
301
302
303
304
305
306 if (startCmp == 0 && endCmp == 0) {
307
308 return new Split<>(null, null);
309 } else if (startCmp < 1 && endCmp < 1) {
310
311 return new Split<>(this, null);
312 } else if (startCmp > -1 && endCmp > -1) {
313
314 return new Split<>(null, this);
315 }
316
317
318 final TestPoint2D intersection = splitter.intersection(line);
319 final double intersectionAbscissa = line.toSubspaceValue(intersection);
320
321 final TestLineSegment startSegment = new TestLineSegment(start, intersectionAbscissa, line);
322 final TestLineSegment endSegment = new TestLineSegment(intersectionAbscissa, end, line);
323
324 final TestLineSegment minus = (startCmp > 0) ? endSegment : startSegment;
325 final TestLineSegment plus = (startCmp > 0) ? startSegment : endSegment;
326
327 return new Split<>(minus, plus);
328 }
329 }