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