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