1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.geometry.spherical.twod;
18
19 import java.util.ArrayList;
20 import java.util.Collections;
21 import java.util.List;
22 import java.util.stream.Stream;
23 import java.util.stream.StreamSupport;
24
25 import org.apache.commons.geometry.core.partitioning.Hyperplane;
26 import org.apache.commons.geometry.core.partitioning.Split;
27 import org.apache.commons.geometry.core.partitioning.bsp.AbstractBSPTree;
28 import org.apache.commons.geometry.core.partitioning.bsp.AbstractRegionBSPTree;
29 import org.apache.commons.geometry.euclidean.threed.Vector3D;
30 import org.apache.commons.numbers.core.Precision;
31 import org.apache.commons.numbers.core.Sum;
32
33
34
35 public class RegionBSPTree2S extends AbstractRegionBSPTree<Point2S, RegionBSPTree2S.RegionNode2S>
36 implements BoundarySource2S {
37
38 private static final double FULL_SIZE = 4 * Math.PI;
39
40
41 private List<GreatArcPath> boundaryPaths;
42
43
44
45 public RegionBSPTree2S() {
46 this(false);
47 }
48
49
50
51
52
53
54 public RegionBSPTree2S(final boolean full) {
55 super(full);
56 }
57
58
59
60
61
62 public RegionBSPTree2S copy() {
63 final RegionBSPTree2S result = RegionBSPTree2S.empty();
64 result.copy(this);
65
66 return result;
67 }
68
69
70 @Override
71 public Iterable<GreatArc> boundaries() {
72 return createBoundaryIterable(GreatArc.class::cast);
73 }
74
75
76 @Override
77 public Stream<GreatArc> boundaryStream() {
78 return StreamSupport.stream(boundaries().spliterator(), false);
79 }
80
81
82 @Override
83 public List<GreatArc> getBoundaries() {
84 return createBoundaryList(GreatArc.class::cast);
85 }
86
87
88
89
90
91
92 public List<GreatArcPath> getBoundaryPaths() {
93 if (boundaryPaths == null) {
94 boundaryPaths = Collections.unmodifiableList(computeBoundaryPaths());
95 }
96 return boundaryPaths;
97 }
98
99
100
101
102
103
104
105 public List<ConvexArea2S> toConvex() {
106 final List<ConvexArea2S> result = new ArrayList<>();
107
108 toConvexRecursive(getRoot(), ConvexArea2S.full(), result);
109
110 return result;
111 }
112
113
114
115
116
117
118
119
120 private void toConvexRecursive(final RegionNode2S node, final ConvexArea2S nodeArea,
121 final List<? super ConvexArea2S> result) {
122 if (node.isLeaf()) {
123
124 if (node.isInside()) {
125 result.add(nodeArea);
126 }
127 } else {
128
129 final Split<ConvexArea2S> split = nodeArea.split(node.getCutHyperplane());
130
131 toConvexRecursive(node.getMinus(), split.getMinus(), result);
132 toConvexRecursive(node.getPlus(), split.getPlus(), result);
133 }
134 }
135
136
137 @Override
138 public Split<RegionBSPTree2S> split(final Hyperplane<Point2S> splitter) {
139 return split(splitter, empty(), empty());
140 }
141
142
143 @Override
144 public Point2S project(final Point2S pt) {
145
146
147 final BoundaryProjector2S projector = new BoundaryProjector2S(pt);
148 accept(projector);
149
150 return projector.getProjected();
151 }
152
153
154
155 @Override
156 public RegionBSPTree2S toTree() {
157 return this;
158 }
159
160
161 @Override
162 protected RegionSizeProperties<Point2S> computeRegionSizeProperties() {
163
164 if (isFull()) {
165 return new RegionSizeProperties<>(FULL_SIZE, null);
166 } else if (isEmpty()) {
167 return new RegionSizeProperties<>(0, null);
168 }
169
170 final List<ConvexArea2S> areas = toConvex();
171 final Precision.DoubleEquivalence precision = ((GreatArc) getRoot().getCut()).getPrecision();
172
173 final Sum sizeSum = Sum.create();
174 final Vector3D.Sum centroidVectorSum = Vector3D.Sum.create();
175
176 double maxCentroidVectorWeightSq = 0.0;
177
178 for (final ConvexArea2S area : areas) {
179 sizeSum.add(area.getSize());
180
181 final Vector3D areaCentroidVector = area.getWeightedCentroidVector();
182 maxCentroidVectorWeightSq = Math.max(maxCentroidVectorWeightSq, areaCentroidVector.normSq());
183
184 centroidVectorSum.add(areaCentroidVector);
185 }
186
187 final double size = sizeSum.getAsDouble();
188 final Vector3D centroidVector = centroidVectorSum.get();
189
190
191
192
193
194
195 final Point2S centroid;
196 if (centroidVector.normSq() < maxCentroidVectorWeightSq &&
197 centroidVector.eq(Vector3D.ZERO, precision)) {
198 centroid = null;
199 } else {
200 centroid = Point2S.from(centroidVector);
201 }
202
203 return new RegionSizeProperties<>(size, centroid);
204 }
205
206
207 @Override
208 protected RegionNode2S createNode() {
209 return new RegionNode2S(this);
210 }
211
212
213 @Override
214 protected void invalidate() {
215 super.invalidate();
216
217 boundaryPaths = null;
218 }
219
220
221
222
223 private List<GreatArcPath> computeBoundaryPaths() {
224 final InteriorAngleGreatArcConnector connector = new InteriorAngleGreatArcConnector.Minimize();
225 return connector.connectAll(boundaries());
226 }
227
228
229
230
231 public static RegionBSPTree2S empty() {
232 return new RegionBSPTree2S(false);
233 }
234
235
236
237
238
239 public static RegionBSPTree2S full() {
240 return new RegionBSPTree2S(true);
241 }
242
243
244
245
246
247
248
249 public static RegionBSPTree2S from(final Iterable<GreatArc> boundaries) {
250 return from(boundaries, false);
251 }
252
253
254
255
256
257
258
259
260 public static RegionBSPTree2S from(final Iterable<GreatArc> boundaries, final boolean full) {
261 final RegionBSPTree2S tree = new RegionBSPTree2S(full);
262 tree.insert(boundaries);
263
264 return tree;
265 }
266
267
268
269 public static final class RegionNode2S extends AbstractRegionBSPTree.AbstractRegionNode<Point2S, RegionNode2S> {
270
271
272
273 private RegionNode2S(final AbstractBSPTree<Point2S, RegionNode2S> tree) {
274 super(tree);
275 }
276
277
278
279
280
281
282 public ConvexArea2S getNodeRegion() {
283 ConvexArea2S area = ConvexArea2S.full();
284
285 RegionNode2S child = this;
286 RegionNode2S parent;
287
288 while ((parent = child.getParent()) != null) {
289 final Split<ConvexArea2S> split = area.split(parent.getCutHyperplane());
290
291 area = child.isMinus() ? split.getMinus() : split.getPlus();
292
293 child = parent;
294 }
295
296 return area;
297 }
298
299
300 @Override
301 protected RegionNode2S getSelf() {
302 return this;
303 }
304 }
305
306
307
308 private static final class BoundaryProjector2S extends BoundaryProjector<Point2S, RegionNode2S> {
309
310
311
312 BoundaryProjector2S(final Point2S point) {
313 super(point);
314 }
315
316
317 @Override
318 protected Point2S disambiguateClosestPoint(final Point2S target, final Point2S a, final Point2S b) {
319
320 final int cmp = Point2S.POLAR_AZIMUTH_ASCENDING_ORDER.compare(a, b);
321 return cmp < 0 ? a : b;
322 }
323 }
324 }