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