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.ArrayList;
25 import java.util.Collection;
26 import java.util.Comparator;
27 import java.util.HashMap;
28 import java.util.Iterator;
29 import java.util.Map;
30 import java.util.TreeSet;
31
32 import org.hipparchus.geometry.Point;
33 import org.hipparchus.geometry.Space;
34
35
36
37
38
39
40
41
42
43
44
45
46
47 public abstract class AbstractRegion<S extends Space,
48 P extends Point<S, P>,
49 H extends Hyperplane<S, P, H, I>,
50 I extends SubHyperplane<S, P, H, I>,
51 T extends Space, Q extends Point<T, Q>,
52 F extends Hyperplane<T, Q, F, J>,
53 J extends SubHyperplane<T, Q, F, J>>
54 implements Region<S, P, H, I> {
55
56
57 private final BSPTree<S, P, H, I> tree;
58
59
60 private final double tolerance;
61
62
63 private double size;
64
65
66 private P barycenter;
67
68
69
70
71 protected AbstractRegion(final double tolerance) {
72 this.tree = new BSPTree<>(Boolean.TRUE);
73 this.tolerance = tolerance;
74 }
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89 protected AbstractRegion(final BSPTree<S, P, H, I> tree, final double tolerance) {
90 this.tree = tree;
91 this.tolerance = tolerance;
92 }
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114 protected AbstractRegion(final Collection<I> boundary, final double tolerance) {
115
116 this.tolerance = tolerance;
117
118 if (boundary.isEmpty()) {
119
120
121 tree = new BSPTree<>(Boolean.TRUE);
122
123 } else {
124
125
126
127
128 final TreeSet<I> ordered = new TreeSet<>(new Comparator<I>() {
129
130 @Override
131 public int compare(final I o1, final I o2) {
132 final double size1 = o1.getSize();
133 final double size2 = o2.getSize();
134 return (size2 < size1) ? -1 : ((o1 == o2) ? 0 : +1);
135 }
136 });
137 ordered.addAll(boundary);
138
139
140 tree = new BSPTree<>();
141 insertCuts(tree, ordered);
142
143
144 tree.visit(new BSPTreeVisitor<S, P, H, I>() {
145
146
147 @Override
148 public Order visitOrder(final BSPTree<S, P, H, I> node) {
149 return Order.PLUS_SUB_MINUS;
150 }
151
152
153 @Override
154 public void visitInternalNode(final BSPTree<S, P, H, I> node) {
155 }
156
157
158 @Override
159 public void visitLeafNode(final BSPTree<S, P, H, I> node) {
160 if (node.getParent() == null || node == node.getParent().getMinus()) {
161 node.setAttribute(Boolean.TRUE);
162 } else {
163 node.setAttribute(Boolean.FALSE);
164 }
165 }
166 });
167
168 }
169
170 }
171
172
173
174
175
176
177 public AbstractRegion(final H[] hyperplanes, final double tolerance) {
178 this.tolerance = tolerance;
179 if ((hyperplanes == null) || (hyperplanes.length == 0)) {
180 tree = new BSPTree<>(Boolean.FALSE);
181 } else {
182
183
184 tree = hyperplanes[0].wholeSpace().getTree(false);
185
186
187 BSPTree<S, P, H, I> node = tree;
188 node.setAttribute(Boolean.TRUE);
189 for (final H hyperplane : hyperplanes) {
190 if (node.insertCut(hyperplane)) {
191 node.setAttribute(null);
192 node.getPlus().setAttribute(Boolean.FALSE);
193 node = node.getMinus();
194 node.setAttribute(Boolean.TRUE);
195 }
196 }
197
198 }
199
200 }
201
202
203 @Override
204 public abstract AbstractRegion<S, P, H, I, T, Q, F, J> buildNew(BSPTree<S, P, H, I> newTree);
205
206
207
208
209 public double getTolerance() {
210 return tolerance;
211 }
212
213
214
215
216
217
218
219 private void insertCuts(final BSPTree<S, P, H, I> node, final Collection<I> boundary) {
220
221 final Iterator<I> iterator = boundary.iterator();
222
223
224 H inserted = null;
225 while ((inserted == null) && iterator.hasNext()) {
226 inserted = iterator.next().getHyperplane();
227 if (!node.insertCut(inserted.copySelf())) {
228 inserted = null;
229 }
230 }
231
232 if (!iterator.hasNext()) {
233 return;
234 }
235
236
237 final ArrayList<I> plusList = new ArrayList<>();
238 final ArrayList<I> minusList = new ArrayList<>();
239 while (iterator.hasNext()) {
240 final I other = iterator.next();
241 final SubHyperplane.SplitSubHyperplane<S, P, H, I> split = other.split(inserted);
242 switch (split.getSide()) {
243 case PLUS:
244 plusList.add(other);
245 break;
246 case MINUS:
247 minusList.add(other);
248 break;
249 case BOTH:
250 plusList.add(split.getPlus());
251 minusList.add(split.getMinus());
252 break;
253 default:
254
255 }
256 }
257
258
259 insertCuts(node.getPlus(), plusList);
260 insertCuts(node.getMinus(), minusList);
261
262 }
263
264
265 @Override
266 public AbstractRegion<S, P, H, I, T, Q, F, J> copySelf() {
267 return buildNew(tree.copySelf());
268 }
269
270
271 @Override
272 public boolean isEmpty() {
273 return isEmpty(tree);
274 }
275
276
277 @Override
278 public boolean isEmpty(final BSPTree<S, P, H, I> node) {
279
280
281
282
283
284 if (node.getCut() == null) {
285
286 return !((Boolean) node.getAttribute());
287 }
288
289
290 return isEmpty(node.getMinus()) && isEmpty(node.getPlus());
291
292 }
293
294
295 @Override
296 public boolean isFull() {
297 return isFull(tree);
298 }
299
300
301 @Override
302 public boolean isFull(final BSPTree<S, P, H, I> node) {
303
304
305
306
307
308 if (node.getCut() == null) {
309
310 return (Boolean) node.getAttribute();
311 }
312
313
314 return isFull(node.getMinus()) && isFull(node.getPlus());
315
316 }
317
318
319 @Override
320 public boolean contains(final Region<S, P, H, I> region) {
321 return new RegionFactory<S, P, H, I>().difference(region, this).isEmpty();
322 }
323
324
325
326 @Override
327 public BoundaryProjection<S, P> projectToBoundary(final P point) {
328 final BoundaryProjector<S, P, H, I, T, Q, F, J> projector = new BoundaryProjector<>(point);
329 getTree(true).visit(projector);
330 return projector.getProjection();
331 }
332
333
334 @Override
335 public Location checkPoint(final P point) {
336 return checkPoint(tree, point);
337 }
338
339
340
341
342
343
344
345
346 protected Location checkPoint(final BSPTree<S, P, H, I> node, final P point) {
347 final BSPTree<S, P, H, I> cell = node.getCell(point, tolerance);
348 if (cell.getCut() == null) {
349
350 return ((Boolean) cell.getAttribute()) ? Location.INSIDE : Location.OUTSIDE;
351 }
352
353
354 final Location minusCode = checkPoint(cell.getMinus(), point);
355 final Location plusCode = checkPoint(cell.getPlus(), point);
356 return (minusCode == plusCode) ? minusCode : Location.BOUNDARY;
357
358 }
359
360
361 @Override
362 public BSPTree<S, P, H, I> getTree(final boolean includeBoundaryAttributes) {
363 if (includeBoundaryAttributes && (tree.getCut() != null) && (tree.getAttribute() == null)) {
364
365 tree.visit(new BoundaryBuilder<>());
366 }
367 return tree;
368 }
369
370
371 @Override
372 public double getBoundarySize() {
373 final BoundarySizeVisitor<S, P, H, I> visitor = new BoundarySizeVisitor<>();
374 getTree(true).visit(visitor);
375 return visitor.getSize();
376 }
377
378
379 @Override
380 public double getSize() {
381 if (barycenter == null) {
382 computeGeometricalProperties();
383 }
384 return size;
385 }
386
387
388
389
390 protected void setSize(final double size) {
391 this.size = size;
392 }
393
394
395 @Override
396 public P getBarycenter() {
397 if (barycenter == null) {
398 computeGeometricalProperties();
399 }
400 return barycenter;
401 }
402
403
404
405
406 protected void setBarycenter(final P barycenter) {
407 this.barycenter = barycenter;
408 }
409
410
411
412
413 protected abstract void computeGeometricalProperties();
414
415
416 @Override
417 public I intersection(final I sub) {
418 return recurseIntersection(tree, sub);
419 }
420
421
422
423
424
425
426
427 private I recurseIntersection(final BSPTree<S, P, H, I> node, final I sub) {
428
429 if (node.getCut() == null) {
430 return (Boolean) node.getAttribute() ? sub.copySelf() : null;
431 }
432
433 final H hyperplane = node.getCut().getHyperplane();
434 final SubHyperplane.SplitSubHyperplane<S, P, H, I> split = sub.split(hyperplane);
435 if (split.getPlus() != null) {
436 if (split.getMinus() != null) {
437
438 final I plus = recurseIntersection(node.getPlus(), split.getPlus());
439 final I minus = recurseIntersection(node.getMinus(), split.getMinus());
440 if (plus == null) {
441 return minus;
442 } else if (minus == null) {
443 return plus;
444 } else {
445 return plus.reunite(minus);
446 }
447 } else {
448
449 return recurseIntersection(node.getPlus(), sub);
450 }
451 } else if (split.getMinus() != null) {
452
453 return recurseIntersection(node.getMinus(), sub);
454 } else {
455
456 return recurseIntersection(node.getPlus(),
457 recurseIntersection(node.getMinus(), sub));
458 }
459
460 }
461
462
463
464
465
466
467
468
469
470
471
472 public AbstractRegion<S, P, H, I, T, Q, F, J> applyTransform(final Transform<S, P, H, I, T, Q, F, J> transform) {
473
474
475 final Map<BSPTree<S, P, H, I>, BSPTree<S, P, H, I>> map = new HashMap<>();
476 final BSPTree<S, P, H, I> transformedTree = recurseTransform(getTree(false), transform, map);
477
478
479 for (final Map.Entry<BSPTree<S, P, H, I>, BSPTree<S, P, H, I>> entry : map.entrySet()) {
480 if (entry.getKey().getCut() != null) {
481 @SuppressWarnings("unchecked")
482 BoundaryAttribute<S, P, H, I> original = (BoundaryAttribute<S, P, H, I>) entry.getKey().getAttribute();
483 if (original != null) {
484 @SuppressWarnings("unchecked")
485 BoundaryAttribute<S, P, H, I> transformed = (BoundaryAttribute<S, P, H, I>) entry.getValue().getAttribute();
486 for (final BSPTree<S, P, H, I> splitter : original.getSplitters()) {
487 transformed.getSplitters().add(map.get(splitter));
488 }
489 }
490 }
491 }
492
493 return buildNew(transformedTree);
494
495 }
496
497
498
499
500
501
502
503 @SuppressWarnings("unchecked")
504 private BSPTree<S, P, H, I> recurseTransform(final BSPTree<S, P, H, I> node, final Transform<S, P, H, I, T, Q, F, J> transform,
505 final Map<BSPTree<S, P, H, I>, BSPTree<S, P, H, I>> map) {
506
507 final BSPTree<S, P, H, I> transformedNode;
508 if (node.getCut() == null) {
509 transformedNode = new BSPTree<>(node.getAttribute());
510 } else {
511
512 final I sub = node.getCut();
513 final I tSub = ((AbstractSubHyperplane<S, P, H, I, T, Q, F, J>) sub).applyTransform(transform);
514 BoundaryAttribute<S, P, H, I> attribute = (BoundaryAttribute<S, P, H, I>) node.getAttribute();
515 if (attribute != null) {
516 final I tPO = (attribute.getPlusOutside() == null) ?
517 null : ((AbstractSubHyperplane<S, P, H, I, T, Q, F, J>) attribute.getPlusOutside()).applyTransform(transform);
518 final I tPI = (attribute.getPlusInside() == null) ?
519 null : ((AbstractSubHyperplane<S, P, H, I, T, Q, F, J>) attribute.getPlusInside()).applyTransform(transform);
520
521 attribute = new BoundaryAttribute<>(tPO, tPI, new NodesSet<>());
522 }
523
524 transformedNode = new BSPTree<>(tSub,
525 recurseTransform(node.getPlus(), transform, map),
526 recurseTransform(node.getMinus(), transform, map),
527 attribute);
528 }
529
530 map.put(node, transformedNode);
531 return transformedNode;
532
533 }
534
535 }