1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.geometry.euclidean;
18
19 import java.util.Arrays;
20 import java.util.Collections;
21 import java.util.List;
22 import java.util.Objects;
23 import java.util.function.ToDoubleBiFunction;
24
25 import org.apache.commons.geometry.core.Embedding;
26 import org.apache.commons.geometry.core.Point;
27 import org.apache.commons.geometry.core.Region;
28 import org.apache.commons.geometry.core.RegionLocation;
29 import org.apache.commons.geometry.euclidean.oned.Vector1D;
30 import org.apache.commons.numbers.core.Precision;
31
32
33
34
35
36
37 public abstract class AbstractNSphere<V extends EuclideanVector<V>> implements Region<V> {
38
39
40 private final V center;
41
42
43 private final double radius;
44
45
46 private final Precision.DoubleEquivalence precision;
47
48
49
50
51
52
53
54
55 protected AbstractNSphere(final V center, final double radius, final Precision.DoubleEquivalence precision) {
56 if (!center.isFinite()) {
57 throw new IllegalArgumentException("Illegal center point: " + center);
58 }
59 if (!Double.isFinite(radius) || precision.lte(radius, 0.0)) {
60 throw new IllegalArgumentException("Illegal radius: " + radius);
61 }
62
63 this.center = center;
64 this.radius = radius;
65 this.precision = precision;
66 }
67
68
69
70
71 public V getCenter() {
72 return center;
73 }
74
75
76
77
78 public double getRadius() {
79 return radius;
80 }
81
82
83
84
85
86 public Precision.DoubleEquivalence getPrecision() {
87 return precision;
88 }
89
90
91
92
93
94 @Override
95 public boolean isFull() {
96 return false;
97 }
98
99
100
101
102
103 @Override
104 public boolean isEmpty() {
105 return false;
106 }
107
108
109
110
111
112 @Override
113 public V getCentroid() {
114 return getCenter();
115 }
116
117
118 @Override
119 public RegionLocation classify(final V pt) {
120 final double dist = ((Point<V>) center).distance(pt);
121 final int cmp = precision.compare(dist, radius);
122 if (cmp < 0) {
123 return RegionLocation.INSIDE;
124 } else if (cmp > 0) {
125 return RegionLocation.OUTSIDE;
126 }
127 return RegionLocation.BOUNDARY;
128 }
129
130
131 @Override
132 public int hashCode() {
133 return Objects.hash(center, radius, precision);
134 }
135
136
137 @Override
138 public boolean equals(final Object obj) {
139 if (this == obj) {
140 return true;
141 } else if (obj == null || !obj.getClass().equals(this.getClass())) {
142 return false;
143 }
144
145 final AbstractNSphere<?> other = (AbstractNSphere<?>) obj;
146
147 return Objects.equals(this.center, other.center) &&
148 Double.compare(this.radius, other.radius) == 0 &&
149 Objects.equals(this.getPrecision(), other.getPrecision());
150 }
151
152
153 @Override
154 public String toString() {
155 final StringBuilder sb = new StringBuilder(30);
156 sb.append(this.getClass().getSimpleName())
157 .append("[center= ")
158 .append(center)
159 .append(", radius= ")
160 .append(radius)
161 .append(']');
162
163 return sb.toString();
164 }
165
166
167
168
169
170
171
172
173
174 protected V project(final V pt, final V defaultVector) {
175 V vec = center.vectorTo(pt);
176 if (vec.equals(vec.getZero())) {
177
178
179 vec = defaultVector;
180 }
181
182 return vec.withNorm(radius).add(center);
183 }
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205 protected <L extends Embedding<V, Vector1D>> List<V> intersections(final L line,
206 final ToDoubleBiFunction<L, V> abscissaFn, final ToDoubleBiFunction<L, V> distanceFn) {
207
208 final double dist = distanceFn.applyAsDouble(line, center);
209
210 final int cmp = precision.compare(dist, radius);
211 if (cmp <= 0) {
212
213 final double abscissa = abscissaFn.applyAsDouble(line, center);
214 final double abscissaDelta = Math.sqrt((radius * radius) - (dist * dist));
215
216 final V p0 = line.toSpace(Vector1D.of(abscissa - abscissaDelta));
217 if (cmp < 0) {
218
219 final V p1 = line.toSpace(Vector1D.of(abscissa + abscissaDelta));
220
221 return Arrays.asList(p0, p1);
222 }
223
224
225 return Collections.singletonList(p0);
226 }
227
228
229 return Collections.emptyList();
230 }
231
232
233
234
235
236
237
238
239
240
241 protected <L extends Embedding<V, Vector1D>> V firstIntersection(final L line,
242 final ToDoubleBiFunction<L, V> abscissaFn, final ToDoubleBiFunction<L, V> distanceFn) {
243
244 final double dist = distanceFn.applyAsDouble(line, center);
245
246 final int cmp = precision.compare(dist, radius);
247 if (cmp <= 0) {
248
249 final double abscissa = abscissaFn.applyAsDouble(line, center);
250 final double abscissaDelta = Math.sqrt((radius * radius) - (dist * dist));
251
252 return line.toSpace(Vector1D.of(abscissa - abscissaDelta));
253 }
254
255 return null;
256 }
257 }