1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package org.hipparchus.geometry.partitioning;
23
24 import java.util.HashMap;
25 import java.util.Map;
26
27 import org.hipparchus.exception.MathIllegalArgumentException;
28 import org.hipparchus.geometry.LocalizedGeometryFormats;
29 import org.hipparchus.geometry.Point;
30 import org.hipparchus.geometry.Space;
31 import org.hipparchus.geometry.partitioning.BSPTree.VanishingCutHandler;
32 import org.hipparchus.geometry.partitioning.Region.Location;
33 import org.hipparchus.geometry.partitioning.SubHyperplane.SplitSubHyperplane;
34
35
36
37
38
39
40
41
42
43 public class RegionFactory<S extends Space, P extends Point<S, P>, H extends Hyperplane<S, P, H, I>, I extends SubHyperplane<S, P, H, I>> {
44
45
46 private final NodesCleaner nodeCleaner;
47
48
49
50 public RegionFactory() {
51 nodeCleaner = new NodesCleaner();
52 }
53
54
55
56
57
58 @SafeVarargs
59 public final Region<S, P, H, I> buildConvex(final H... hyperplanes) {
60 if ((hyperplanes == null) || (hyperplanes.length == 0)) {
61 return null;
62 }
63
64
65 final Region<S, P, H, I> region = hyperplanes[0].wholeSpace();
66
67
68 BSPTree<S, P, H, I> node = region.getTree(false);
69 node.setAttribute(Boolean.TRUE);
70 for (final H hyperplane : hyperplanes) {
71 if (node.insertCut(hyperplane)) {
72 node.setAttribute(null);
73 node.getPlus().setAttribute(Boolean.FALSE);
74 node = node.getMinus();
75 node.setAttribute(Boolean.TRUE);
76 } else {
77
78
79
80 I s = hyperplane.wholeHyperplane();
81 for (BSPTree<S, P, H, I> tree = node; tree.getParent() != null && s != null; tree = tree.getParent()) {
82 final H other = tree.getParent().getCut().getHyperplane();
83 final SplitSubHyperplane<S, P, H, I> split = s.split(other);
84 switch (split.getSide()) {
85 case HYPER :
86
87 if (!hyperplane.sameOrientationAs(other)) {
88
89
90 return getComplement(hyperplanes[0].wholeSpace());
91 }
92
93 break;
94 case PLUS :
95
96
97 throw new MathIllegalArgumentException(LocalizedGeometryFormats.NOT_CONVEX_HYPERPLANES);
98 default :
99 s = split.getMinus();
100 }
101 }
102 }
103 }
104
105 return region;
106
107 }
108
109
110
111
112
113
114
115
116 public Region<S, P, H, I> union(final Region<S, P, H, I> region1, final Region<S, P, H, I> region2) {
117 final BSPTree<S, P, H, I> tree =
118 region1.getTree(false).merge(region2.getTree(false), new UnionMerger());
119 tree.visit(nodeCleaner);
120 return region1.buildNew(tree);
121 }
122
123
124
125
126
127
128
129
130 public Region<S, P, H, I> intersection(final Region<S, P, H, I> region1, final Region<S, P, H, I> region2) {
131 final BSPTree<S, P, H, I> tree =
132 region1.getTree(false).merge(region2.getTree(false), new IntersectionMerger(region1, region2));
133 tree.visit(nodeCleaner);
134 return region1.buildNew(tree);
135 }
136
137
138
139
140
141
142
143
144 public Region<S, P, H, I> xor(final Region<S, P, H, I> region1, final Region<S, P, H, I> region2) {
145 final BSPTree<S, P, H, I> tree =
146 region1.getTree(false).merge(region2.getTree(false), new XorMerger());
147 tree.visit(nodeCleaner);
148 return region1.buildNew(tree);
149 }
150
151
152
153
154
155
156
157
158 public Region<S, P, H, I> difference(final Region<S, P, H, I> region1, final Region<S, P, H, I> region2) {
159 final BSPTree<S, P, H, I> tree =
160 region1.getTree(false).merge(region2.getTree(false), new DifferenceMerger(region1, region2));
161 tree.visit(nodeCleaner);
162 return region1.buildNew(tree);
163 }
164
165
166
167
168
169
170 public Region<S, P, H, I> getComplement(final Region<S, P, H, I> region) {
171 return region.buildNew(recurseComplement(region.getTree(false)));
172 }
173
174
175
176
177
178 private BSPTree<S, P, H, I> recurseComplement(final BSPTree<S, P, H, I> node) {
179
180
181 final Map<BSPTree<S, P, H, I>, BSPTree<S, P, H, I>> map = new HashMap<>();
182 final BSPTree<S, P, H, I> transformedTree = recurseComplement(node, map);
183
184
185 for (final Map.Entry<BSPTree<S, P, H, I>, BSPTree<S, P, H, I>> entry : map.entrySet()) {
186 if (entry.getKey().getCut() != null) {
187 @SuppressWarnings("unchecked")
188 BoundaryAttribute<S, P, H, I> original = (BoundaryAttribute<S, P, H, I>) entry.getKey().getAttribute();
189 if (original != null) {
190 @SuppressWarnings("unchecked")
191 BoundaryAttribute<S, P, H, I> transformed = (BoundaryAttribute<S, P, H, I>) entry.getValue().getAttribute();
192 for (final BSPTree<S, P, H, I> splitter : original.getSplitters()) {
193 transformed.getSplitters().add(map.get(splitter));
194 }
195 }
196 }
197 }
198
199 return transformedTree;
200
201 }
202
203
204
205
206
207
208 private BSPTree<S, P, H, I> recurseComplement(final BSPTree<S, P, H, I> node,
209 final Map<BSPTree<S, P, H, I>, BSPTree<S, P, H, I>> map) {
210
211 final BSPTree<S, P, H, I> transformedNode;
212 if (node.getCut() == null) {
213 transformedNode = new BSPTree<>(((Boolean) node.getAttribute()) ? Boolean.FALSE : Boolean.TRUE);
214 } else {
215
216 @SuppressWarnings("unchecked")
217 BoundaryAttribute<S, P, H, I> attribute = (BoundaryAttribute<S, P, H, I>) node.getAttribute();
218 if (attribute != null) {
219 final I plusOutside = (attribute.getPlusInside() == null) ? null : attribute.getPlusInside().copySelf();
220 final I plusInside = (attribute.getPlusOutside() == null) ? null : attribute.getPlusOutside().copySelf();
221
222 attribute = new BoundaryAttribute<>(plusOutside, plusInside, new NodesSet<>());
223 }
224
225 transformedNode = new BSPTree<>(node.getCut().copySelf(),
226 recurseComplement(node.getPlus(), map),
227 recurseComplement(node.getMinus(), map),
228 attribute);
229 }
230
231 map.put(node, transformedNode);
232 return transformedNode;
233
234 }
235
236
237 private abstract class FixingMerger implements BSPTree.LeafMerger<S, P, H, I>, VanishingCutHandler<S, P, H, I> {
238
239
240 private final Region<S, P, H, I> region1;
241
242
243 private final Region<S, P, H, I> region2;
244
245
246
247
248
249 protected FixingMerger(final Region<S, P, H, I> region1, final Region<S, P, H, I> region2) {
250 this.region1 = region1.copySelf();
251 this.region2 = region2.copySelf();
252 }
253
254
255 @Override
256 public BSPTree<S, P, H, I> fixNode(final BSPTree<S, P, H, I> node) {
257
258 final BSPTree.InteriorPoint<S, P> interior = node.getInteriorPoint(node.getParent().getCut().getHyperplane().arbitraryPoint());
259 return new BSPTree<>(shouldBeInside(region1.checkPoint(interior.getPoint()), region2.checkPoint(interior.getPoint())));
260 }
261
262
263
264
265
266
267
268 protected abstract boolean shouldBeInside(Location location1, Location location2);
269
270 }
271
272
273 private class UnionMerger implements BSPTree.LeafMerger<S, P, H, I> {
274
275 @Override
276 public BSPTree<S, P, H, I> merge(final BSPTree<S, P, H, I> leaf, final BSPTree<S, P, H, I> tree,
277 final BSPTree<S, P, H, I> parentTree,
278 final boolean isPlusChild, final boolean leafFromInstance) {
279 if ((Boolean) leaf.getAttribute()) {
280
281 leaf.insertInTree(parentTree, isPlusChild, new VanishingToLeaf(true));
282 return leaf;
283 }
284
285 tree.insertInTree(parentTree, isPlusChild, new VanishingToLeaf(false));
286 return tree;
287 }
288 }
289
290
291 private class IntersectionMerger extends FixingMerger {
292
293
294
295
296
297 IntersectionMerger(final Region<S, P, H, I> region1, final Region<S, P, H, I> region2) {
298 super(region1, region2);
299 }
300
301
302 @Override
303 public BSPTree<S, P, H, I> merge(final BSPTree<S, P, H, I> leaf, final BSPTree<S, P, H, I> tree,
304 final BSPTree<S, P, H, I> parentTree,
305 final boolean isPlusChild, final boolean leafFromInstance) {
306 if ((Boolean) leaf.getAttribute()) {
307
308 tree.insertInTree(parentTree, isPlusChild, this);
309 return tree;
310 }
311
312 leaf.insertInTree(parentTree, isPlusChild, this);
313 return leaf;
314 }
315
316
317 @Override
318 protected boolean shouldBeInside(final Location location1, final Location location2)
319 {
320 return !(location1.equals(Location.OUTSIDE) || location2.equals(Location.OUTSIDE));
321 }
322
323 }
324
325
326 private class XorMerger implements BSPTree.LeafMerger<S, P, H, I> {
327
328 @Override
329 public BSPTree<S, P, H, I> merge(final BSPTree<S, P, H, I> leaf, final BSPTree<S, P, H, I> tree,
330 final BSPTree<S, P, H, I> parentTree, final boolean isPlusChild,
331 final boolean leafFromInstance) {
332 BSPTree<S, P, H, I> t = tree;
333 if ((Boolean) leaf.getAttribute()) {
334
335 t = recurseComplement(t);
336 }
337 t.insertInTree(parentTree, isPlusChild, new VanishingToLeaf(true));
338 return t;
339 }
340 }
341
342
343 private class DifferenceMerger extends FixingMerger {
344
345
346
347
348
349 DifferenceMerger(final Region<S, P, H, I> region1, final Region<S, P, H, I> region2) {
350 super(region1, region2);
351 }
352
353
354 @Override
355 public BSPTree<S, P, H, I> merge(final BSPTree<S, P, H, I> leaf, final BSPTree<S, P, H, I> tree,
356 final BSPTree<S, P, H, I> parentTree, final boolean isPlusChild,
357 final boolean leafFromInstance) {
358 if ((Boolean) leaf.getAttribute()) {
359
360 final BSPTree<S, P, H, I> argTree =
361 recurseComplement(leafFromInstance ? tree : leaf);
362 argTree.insertInTree(parentTree, isPlusChild, this);
363 return argTree;
364 }
365
366 final BSPTree<S, P, H, I> instanceTree =
367 leafFromInstance ? leaf : tree;
368 instanceTree.insertInTree(parentTree, isPlusChild, this);
369 return instanceTree;
370 }
371
372
373 @Override
374 protected boolean shouldBeInside(final Location location1, final Location location2) {
375 return location1 == Location.INSIDE && location2 == Location.OUTSIDE;
376 }
377
378 }
379
380
381 private class NodesCleaner implements BSPTreeVisitor<S, P, H, I> {
382
383
384 @Override
385 public Order visitOrder(final BSPTree<S, P, H, I> node) {
386 return Order.PLUS_SUB_MINUS;
387 }
388
389
390 @Override
391 public void visitInternalNode(final BSPTree<S, P, H, I> node) {
392 node.setAttribute(null);
393 }
394
395
396 @Override
397 public void visitLeafNode(final BSPTree<S, P, H, I> node) {
398 }
399
400 }
401
402
403 private class VanishingToLeaf implements VanishingCutHandler<S, P, H, I> {
404
405
406 private final boolean inside;
407
408
409
410
411 VanishingToLeaf(final boolean inside) {
412 this.inside = inside;
413 }
414
415
416 @Override
417 public BSPTree<S, P, H, I> fixNode(final BSPTree<S, P, H, I> node) {
418 if (node.getPlus().getAttribute().equals(node.getMinus().getAttribute())) {
419
420 return new BSPTree<>(node.getPlus().getAttribute());
421 } else {
422
423 return new BSPTree<>(inside);
424 }
425 }
426
427 }
428
429 }