1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.geometry.euclidean.oned;
18
19 import java.util.Collections;
20 import java.util.List;
21 import java.util.Objects;
22
23 import org.apache.commons.geometry.core.RegionLocation;
24 import org.apache.commons.geometry.core.Transform;
25 import org.apache.commons.geometry.core.partitioning.AbstractHyperplane;
26 import org.apache.commons.geometry.core.partitioning.Hyperplane;
27 import org.apache.commons.geometry.core.partitioning.HyperplaneConvexSubset;
28 import org.apache.commons.geometry.core.partitioning.HyperplaneLocation;
29 import org.apache.commons.geometry.core.partitioning.Split;
30 import org.apache.commons.numbers.core.Precision;
31
32
33
34
35
36
37
38
39
40 public final class OrientedPoint extends AbstractHyperplane<Vector1D> {
41
42 private final Vector1D point;
43
44
45 private final boolean positiveFacing;
46
47
48
49
50
51
52
53 OrientedPoint(final Vector1D point, final boolean positiveFacing, final Precision.DoubleEquivalence precision) {
54 super(precision);
55
56 this.point = point;
57 this.positiveFacing = positiveFacing;
58 }
59
60
61
62
63
64 public Vector1D getPoint() {
65 return point;
66 }
67
68
69
70
71
72
73
74 public double getLocation() {
75 return point.getX();
76 }
77
78
79
80
81 public Vector1D.Unit getDirection() {
82 return positiveFacing ? Vector1D.Unit.PLUS : Vector1D.Unit.MINUS;
83 }
84
85
86
87
88
89
90
91 public boolean isPositiveFacing() {
92 return positiveFacing;
93 }
94
95
96 @Override
97 public OrientedPoint reverse() {
98 return new OrientedPoint(point, !positiveFacing, getPrecision());
99 }
100
101
102 @Override
103 public OrientedPoint transform(final Transform<Vector1D> transform) {
104 final Vector1D transformedPoint = transform.apply(point);
105
106 final Vector1D transformedDir;
107 if (point.isInfinite()) {
108
109 final Vector1D transformedZero = transform.apply(Vector1D.ZERO);
110 final Vector1D transformedZeroDir = transform.apply(getDirection());
111
112 transformedDir = transformedZero.vectorTo(transformedZeroDir);
113 } else {
114 final Vector1D transformedPointPlusDir = transform.apply(point.add(getDirection()));
115 transformedDir = transformedPoint.vectorTo(transformedPointPlusDir);
116 }
117
118 return OrientedPoints.fromPointAndDirection(
119 transformedPoint,
120 transformedDir,
121 getPrecision()
122 );
123 }
124
125
126 @Override
127 public double offset(final Vector1D pt) {
128 return offset(pt.getX());
129 }
130
131
132
133
134
135
136
137 public double offset(final double location) {
138 final double delta = location - point.getX();
139 return positiveFacing ? delta : -delta;
140 }
141
142
143 @Override
144 public HyperplaneLocation classify(final Vector1D pt) {
145 return classify(pt.getX());
146 }
147
148
149
150
151
152
153
154
155 public HyperplaneLocation classify(final double location) {
156 final double offsetValue = offset(location);
157
158 final double cmp = getPrecision().signum(offsetValue);
159 if (cmp > 0) {
160 return HyperplaneLocation.PLUS;
161 } else if (cmp < 0) {
162 return HyperplaneLocation.MINUS;
163 }
164 return HyperplaneLocation.ON;
165 }
166
167
168 @Override
169 public boolean similarOrientation(final Hyperplane<Vector1D> other) {
170 return positiveFacing == ((OrientedPoint) other).positiveFacing;
171 }
172
173
174 @Override
175 public Vector1D project(final Vector1D pt) {
176 return this.point;
177 }
178
179
180
181
182
183
184
185 @Override
186 public HyperplaneConvexSubset<Vector1D> span() {
187 return new OrientedPointConvexSubset(this);
188 }
189
190
191
192
193
194
195
196
197
198
199
200
201
202 public boolean eq(final OrientedPoint other, final Precision.DoubleEquivalence precision) {
203 return point.eq(other.point, precision) &&
204 positiveFacing == other.positiveFacing;
205 }
206
207
208 @Override
209 public int hashCode() {
210 final int prime = 31;
211
212 int result = 1;
213 result = (prime * result) + Objects.hashCode(point);
214 result = (prime * result) + Boolean.hashCode(positiveFacing);
215 result = (prime * result) + Objects.hashCode(getPrecision());
216
217 return result;
218 }
219
220
221 @Override
222 public boolean equals(final Object obj) {
223 if (this == obj) {
224 return true;
225 } else if (!(obj instanceof OrientedPoint)) {
226 return false;
227 }
228
229 final OrientedPoint other = (OrientedPoint) obj;
230
231 return Objects.equals(this.point, other.point) &&
232 this.positiveFacing == other.positiveFacing &&
233 Objects.equals(this.getPrecision(), other.getPrecision());
234 }
235
236
237 @Override
238 public String toString() {
239 final StringBuilder sb = new StringBuilder();
240 sb.append(this.getClass().getSimpleName())
241 .append("[point= ")
242 .append(point)
243 .append(", direction= ")
244 .append(getDirection())
245 .append(']');
246
247 return sb.toString();
248 }
249
250
251
252
253
254 private static class OrientedPointConvexSubset implements HyperplaneConvexSubset<Vector1D> {
255
256 private final OrientedPoint hyperplane;
257
258
259
260
261 OrientedPointConvexSubset(final OrientedPoint hyperplane) {
262 this.hyperplane = hyperplane;
263 }
264
265
266 @Override
267 public OrientedPoint getHyperplane() {
268 return hyperplane;
269 }
270
271
272
273
274
275 @Override
276 public boolean isFull() {
277 return false;
278 }
279
280
281
282
283
284 @Override
285 public boolean isEmpty() {
286 return false;
287 }
288
289
290
291
292
293 @Override
294 public boolean isInfinite() {
295 return false;
296 }
297
298
299
300
301
302 @Override
303 public boolean isFinite() {
304 return true;
305 }
306
307
308
309
310
311 @Override
312 public double getSize() {
313 return 0;
314 }
315
316
317
318
319
320 @Override
321 public Vector1D getCentroid() {
322 return hyperplane.getPoint();
323 }
324
325
326
327
328
329
330
331 @Override
332 public RegionLocation classify(final Vector1D point) {
333 if (hyperplane.contains(point)) {
334 return RegionLocation.BOUNDARY;
335 }
336
337 return RegionLocation.OUTSIDE;
338 }
339
340
341 @Override
342 public Vector1D closest(final Vector1D point) {
343 return hyperplane.project(point);
344 }
345
346
347 @Override
348 public Split<OrientedPointConvexSubset> split(final Hyperplane<Vector1D> splitter) {
349 final HyperplaneLocation side = splitter.classify(hyperplane.getPoint());
350
351 OrientedPointConvexSubset minus = null;
352 OrientedPointConvexSubset plus = null;
353
354 if (side == HyperplaneLocation.MINUS) {
355 minus = this;
356 } else if (side == HyperplaneLocation.PLUS) {
357 plus = this;
358 }
359
360 return new Split<>(minus, plus);
361 }
362
363
364 @Override
365 public List<OrientedPointConvexSubset> toConvex() {
366 return Collections.singletonList(this);
367 }
368
369
370 @Override
371 public OrientedPointConvexSubset transform(final Transform<Vector1D> transform) {
372 return new OrientedPointConvexSubset(getHyperplane().transform(transform));
373 }
374
375
376 @Override
377 public OrientedPointConvexSubset reverse() {
378 return new OrientedPointConvexSubset(hyperplane.reverse());
379 }
380
381
382 @Override
383 public String toString() {
384 final StringBuilder sb = new StringBuilder();
385 sb.append(this.getClass().getSimpleName())
386 .append("[hyperplane= ")
387 .append(hyperplane)
388 .append(']');
389
390 return sb.toString();
391 }
392 }
393 }