001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.math3.geometry.partitioning; 018 019import java.util.ArrayList; 020import java.util.List; 021 022import org.apache.commons.math3.exception.MathIllegalStateException; 023import org.apache.commons.math3.exception.MathInternalError; 024import org.apache.commons.math3.exception.util.LocalizedFormats; 025import org.apache.commons.math3.geometry.Point; 026import org.apache.commons.math3.geometry.Space; 027import org.apache.commons.math3.geometry.Vector; 028import org.apache.commons.math3.util.FastMath; 029 030/** This class represent a Binary Space Partition tree. 031 032 * <p>BSP trees are an efficient way to represent space partitions and 033 * to associate attributes with each cell. Each node in a BSP tree 034 * represents a convex region which is partitioned in two convex 035 * sub-regions at each side of a cut hyperplane. The root tree 036 * contains the complete space.</p> 037 038 * <p>The main use of such partitions is to use a boolean attribute to 039 * define an inside/outside property, hence representing arbitrary 040 * polytopes (line segments in 1D, polygons in 2D and polyhedrons in 041 * 3D) and to operate on them.</p> 042 043 * <p>Another example would be to represent Voronoi tesselations, the 044 * attribute of each cell holding the defining point of the cell.</p> 045 046 * <p>The application-defined attributes are shared among copied 047 * instances and propagated to split parts. These attributes are not 048 * used by the BSP-tree algorithms themselves, so the application can 049 * use them for any purpose. Since the tree visiting method holds 050 * internal and leaf nodes differently, it is possible to use 051 * different classes for internal nodes attributes and leaf nodes 052 * attributes. This should be used with care, though, because if the 053 * tree is modified in any way after attributes have been set, some 054 * internal nodes may become leaf nodes and some leaf nodes may become 055 * internal nodes.</p> 056 057 * <p>One of the main sources for the development of this package was 058 * Bruce Naylor, John Amanatides and William Thibault paper <a 059 * href="http://www.cs.yorku.ca/~amana/research/bsptSetOp.pdf">Merging 060 * BSP Trees Yields Polyhedral Set Operations</a> Proc. Siggraph '90, 061 * Computer Graphics 24(4), August 1990, pp 115-124, published by the 062 * Association for Computing Machinery (ACM).</p> 063 064 * @param <S> Type of the space. 065 066 * @since 3.0 067 */ 068public class BSPTree<S extends Space> { 069 070 /** Cut sub-hyperplane. */ 071 private SubHyperplane<S> cut; 072 073 /** Tree at the plus side of the cut hyperplane. */ 074 private BSPTree<S> plus; 075 076 /** Tree at the minus side of the cut hyperplane. */ 077 private BSPTree<S> minus; 078 079 /** Parent tree. */ 080 private BSPTree<S> parent; 081 082 /** Application-defined attribute. */ 083 private Object attribute; 084 085 /** Build a tree having only one root cell representing the whole space. 086 */ 087 public BSPTree() { 088 cut = null; 089 plus = null; 090 minus = null; 091 parent = null; 092 attribute = null; 093 } 094 095 /** Build a tree having only one root cell representing the whole space. 096 * @param attribute attribute of the tree (may be null) 097 */ 098 public BSPTree(final Object attribute) { 099 cut = null; 100 plus = null; 101 minus = null; 102 parent = null; 103 this.attribute = attribute; 104 } 105 106 /** Build a BSPTree from its underlying elements. 107 * <p>This method does <em>not</em> perform any verification on 108 * consistency of its arguments, it should therefore be used only 109 * when then caller knows what it is doing.</p> 110 * <p>This method is mainly useful to build trees 111 * bottom-up. Building trees top-down is realized with the help of 112 * method {@link #insertCut insertCut}.</p> 113 * @param cut cut sub-hyperplane for the tree 114 * @param plus plus side sub-tree 115 * @param minus minus side sub-tree 116 * @param attribute attribute associated with the node (may be null) 117 * @see #insertCut 118 */ 119 public BSPTree(final SubHyperplane<S> cut, final BSPTree<S> plus, final BSPTree<S> minus, 120 final Object attribute) { 121 this.cut = cut; 122 this.plus = plus; 123 this.minus = minus; 124 this.parent = null; 125 this.attribute = attribute; 126 plus.parent = this; 127 minus.parent = this; 128 } 129 130 /** Insert a cut sub-hyperplane in a node. 131 * <p>The sub-tree starting at this node will be completely 132 * overwritten. The new cut sub-hyperplane will be built from the 133 * intersection of the provided hyperplane with the cell. If the 134 * hyperplane does intersect the cell, the cell will have two 135 * children cells with {@code null} attributes on each side of 136 * the inserted cut sub-hyperplane. If the hyperplane does not 137 * intersect the cell then <em>no</em> cut hyperplane will be 138 * inserted and the cell will be changed to a leaf cell. The 139 * attribute of the node is never changed.</p> 140 * <p>This method is mainly useful when called on leaf nodes 141 * (i.e. nodes for which {@link #getCut getCut} returns 142 * {@code null}), in this case it provides a way to build a 143 * tree top-down (whereas the {@link #BSPTree(SubHyperplane, 144 * BSPTree, BSPTree, Object) 4 arguments constructor} is devoted to 145 * build trees bottom-up).</p> 146 * @param hyperplane hyperplane to insert, it will be chopped in 147 * order to fit in the cell defined by the parent nodes of the 148 * instance 149 * @return true if a cut sub-hyperplane has been inserted (i.e. if 150 * the cell now has two leaf child nodes) 151 * @see #BSPTree(SubHyperplane, BSPTree, BSPTree, Object) 152 */ 153 public boolean insertCut(final Hyperplane<S> hyperplane) { 154 155 if (cut != null) { 156 plus.parent = null; 157 minus.parent = null; 158 } 159 160 final SubHyperplane<S> chopped = fitToCell(hyperplane.wholeHyperplane()); 161 if (chopped == null || chopped.isEmpty()) { 162 cut = null; 163 plus = null; 164 minus = null; 165 return false; 166 } 167 168 cut = chopped; 169 plus = new BSPTree<S>(); 170 plus.parent = this; 171 minus = new BSPTree<S>(); 172 minus.parent = this; 173 return true; 174 175 } 176 177 /** Copy the instance. 178 * <p>The instance created is completely independent of the original 179 * one. A deep copy is used, none of the underlying objects are 180 * shared (except for the nodes attributes and immutable 181 * objects).</p> 182 * @return a new tree, copy of the instance 183 */ 184 public BSPTree<S> copySelf() { 185 186 if (cut == null) { 187 return new BSPTree<S>(attribute); 188 } 189 190 return new BSPTree<S>(cut.copySelf(), plus.copySelf(), minus.copySelf(), 191 attribute); 192 193 } 194 195 /** Get the cut sub-hyperplane. 196 * @return cut sub-hyperplane, null if this is a leaf tree 197 */ 198 public SubHyperplane<S> getCut() { 199 return cut; 200 } 201 202 /** Get the tree on the plus side of the cut hyperplane. 203 * @return tree on the plus side of the cut hyperplane, null if this 204 * is a leaf tree 205 */ 206 public BSPTree<S> getPlus() { 207 return plus; 208 } 209 210 /** Get the tree on the minus side of the cut hyperplane. 211 * @return tree on the minus side of the cut hyperplane, null if this 212 * is a leaf tree 213 */ 214 public BSPTree<S> getMinus() { 215 return minus; 216 } 217 218 /** Get the parent node. 219 * @return parent node, null if the node has no parents 220 */ 221 public BSPTree<S> getParent() { 222 return parent; 223 } 224 225 /** Associate an attribute with the instance. 226 * @param attribute attribute to associate with the node 227 * @see #getAttribute 228 */ 229 public void setAttribute(final Object attribute) { 230 this.attribute = attribute; 231 } 232 233 /** Get the attribute associated with the instance. 234 * @return attribute associated with the node or null if no 235 * attribute has been explicitly set using the {@link #setAttribute 236 * setAttribute} method 237 * @see #setAttribute 238 */ 239 public Object getAttribute() { 240 return attribute; 241 } 242 243 /** Visit the BSP tree nodes. 244 * @param visitor object visiting the tree nodes 245 */ 246 public void visit(final BSPTreeVisitor<S> visitor) { 247 if (cut == null) { 248 visitor.visitLeafNode(this); 249 } else { 250 switch (visitor.visitOrder(this)) { 251 case PLUS_MINUS_SUB: 252 plus.visit(visitor); 253 minus.visit(visitor); 254 visitor.visitInternalNode(this); 255 break; 256 case PLUS_SUB_MINUS: 257 plus.visit(visitor); 258 visitor.visitInternalNode(this); 259 minus.visit(visitor); 260 break; 261 case MINUS_PLUS_SUB: 262 minus.visit(visitor); 263 plus.visit(visitor); 264 visitor.visitInternalNode(this); 265 break; 266 case MINUS_SUB_PLUS: 267 minus.visit(visitor); 268 visitor.visitInternalNode(this); 269 plus.visit(visitor); 270 break; 271 case SUB_PLUS_MINUS: 272 visitor.visitInternalNode(this); 273 plus.visit(visitor); 274 minus.visit(visitor); 275 break; 276 case SUB_MINUS_PLUS: 277 visitor.visitInternalNode(this); 278 minus.visit(visitor); 279 plus.visit(visitor); 280 break; 281 default: 282 throw new MathInternalError(); 283 } 284 285 } 286 } 287 288 /** Fit a sub-hyperplane inside the cell defined by the instance. 289 * <p>Fitting is done by chopping off the parts of the 290 * sub-hyperplane that lie outside of the cell using the 291 * cut-hyperplanes of the parent nodes of the instance.</p> 292 * @param sub sub-hyperplane to fit 293 * @return a new sub-hyperplane, guaranteed to have no part outside 294 * of the instance cell 295 */ 296 private SubHyperplane<S> fitToCell(final SubHyperplane<S> sub) { 297 SubHyperplane<S> s = sub; 298 for (BSPTree<S> tree = this; tree.parent != null && s != null; tree = tree.parent) { 299 if (tree == tree.parent.plus) { 300 s = s.split(tree.parent.cut.getHyperplane()).getPlus(); 301 } else { 302 s = s.split(tree.parent.cut.getHyperplane()).getMinus(); 303 } 304 } 305 return s; 306 } 307 308 /** Get the cell to which a point belongs. 309 * <p>If the returned cell is a leaf node the points belongs to the 310 * interior of the node, if the cell is an internal node the points 311 * belongs to the node cut sub-hyperplane.</p> 312 * @param point point to check 313 * @return the tree cell to which the point belongs 314 * @deprecated as of 3.3, replaced with {@link #getCell(Point, double)} 315 */ 316 @Deprecated 317 public BSPTree<S> getCell(final Vector<S> point) { 318 return getCell((Point<S>) point, 1.0e-10); 319 } 320 321 /** Get the cell to which a point belongs. 322 * <p>If the returned cell is a leaf node the points belongs to the 323 * interior of the node, if the cell is an internal node the points 324 * belongs to the node cut sub-hyperplane.</p> 325 * @param point point to check 326 * @param tolerance tolerance below which points close to a cut hyperplane 327 * are considered to belong to the hyperplane itself 328 * @return the tree cell to which the point belongs 329 */ 330 public BSPTree<S> getCell(final Point<S> point, final double tolerance) { 331 332 if (cut == null) { 333 return this; 334 } 335 336 // position of the point with respect to the cut hyperplane 337 final double offset = cut.getHyperplane().getOffset(point); 338 339 if (FastMath.abs(offset) < tolerance) { 340 return this; 341 } else if (offset <= 0) { 342 // point is on the minus side of the cut hyperplane 343 return minus.getCell(point, tolerance); 344 } else { 345 // point is on the plus side of the cut hyperplane 346 return plus.getCell(point, tolerance); 347 } 348 349 } 350 351 /** Get the cells whose cut sub-hyperplanes are close to the point. 352 * @param point point to check 353 * @param maxOffset offset below which a cut sub-hyperplane is considered 354 * close to the point (in absolute value) 355 * @return close cells (may be empty if all cut sub-hyperplanes are farther 356 * than maxOffset from the point) 357 */ 358 public List<BSPTree<S>> getCloseCuts(final Point<S> point, final double maxOffset) { 359 final List<BSPTree<S>> close = new ArrayList<BSPTree<S>>(); 360 recurseCloseCuts(point, maxOffset, close); 361 return close; 362 } 363 364 /** Get the cells whose cut sub-hyperplanes are close to the point. 365 * @param point point to check 366 * @param maxOffset offset below which a cut sub-hyperplane is considered 367 * close to the point (in absolute value) 368 * @param close list to fill 369 */ 370 private void recurseCloseCuts(final Point<S> point, final double maxOffset, 371 final List<BSPTree<S>> close) { 372 if (cut != null) { 373 374 // position of the point with respect to the cut hyperplane 375 final double offset = cut.getHyperplane().getOffset(point); 376 377 if (offset < -maxOffset) { 378 // point is on the minus side of the cut hyperplane 379 minus.recurseCloseCuts(point, maxOffset, close); 380 } else if (offset > maxOffset) { 381 // point is on the plus side of the cut hyperplane 382 plus.recurseCloseCuts(point, maxOffset, close); 383 } else { 384 // point is close to the cut hyperplane 385 close.add(this); 386 minus.recurseCloseCuts(point, maxOffset, close); 387 plus.recurseCloseCuts(point, maxOffset, close); 388 } 389 390 } 391 } 392 393 /** Perform condensation on a tree. 394 * <p>The condensation operation is not recursive, it must be called 395 * explicitly from leaves to root.</p> 396 */ 397 private void condense() { 398 if ((cut != null) && (plus.cut == null) && (minus.cut == null) && 399 (((plus.attribute == null) && (minus.attribute == null)) || 400 ((plus.attribute != null) && plus.attribute.equals(minus.attribute)))) { 401 attribute = (plus.attribute == null) ? minus.attribute : plus.attribute; 402 cut = null; 403 plus = null; 404 minus = null; 405 } 406 } 407 408 /** Merge a BSP tree with the instance. 409 * <p>All trees are modified (parts of them are reused in the new 410 * tree), it is the responsibility of the caller to ensure a copy 411 * has been done before if any of the former tree should be 412 * preserved, <em>no</em> such copy is done here!</p> 413 * <p>The algorithm used here is directly derived from the one 414 * described in the Naylor, Amanatides and Thibault paper (section 415 * III, Binary Partitioning of a BSP Tree).</p> 416 * @param tree other tree to merge with the instance (will be 417 * <em>unusable</em> after the operation, as well as the 418 * instance itself) 419 * @param leafMerger object implementing the final merging phase 420 * (this is where the semantic of the operation occurs, generally 421 * depending on the attribute of the leaf node) 422 * @return a new tree, result of <code>instance <op> 423 * tree</code>, this value can be ignored if parentTree is not null 424 * since all connections have already been established 425 */ 426 public BSPTree<S> merge(final BSPTree<S> tree, final LeafMerger<S> leafMerger) { 427 return merge(tree, leafMerger, null, false); 428 } 429 430 /** Merge a BSP tree with the instance. 431 * @param tree other tree to merge with the instance (will be 432 * <em>unusable</em> after the operation, as well as the 433 * instance itself) 434 * @param leafMerger object implementing the final merging phase 435 * (this is where the semantic of the operation occurs, generally 436 * depending on the attribute of the leaf node) 437 * @param parentTree parent tree to connect to (may be null) 438 * @param isPlusChild if true and if parentTree is not null, the 439 * resulting tree should be the plus child of its parent, ignored if 440 * parentTree is null 441 * @return a new tree, result of <code>instance <op> 442 * tree</code>, this value can be ignored if parentTree is not null 443 * since all connections have already been established 444 */ 445 private BSPTree<S> merge(final BSPTree<S> tree, final LeafMerger<S> leafMerger, 446 final BSPTree<S> parentTree, final boolean isPlusChild) { 447 if (cut == null) { 448 // cell/tree operation 449 return leafMerger.merge(this, tree, parentTree, isPlusChild, true); 450 } else if (tree.cut == null) { 451 // tree/cell operation 452 return leafMerger.merge(tree, this, parentTree, isPlusChild, false); 453 } else { 454 // tree/tree operation 455 final BSPTree<S> merged = tree.split(cut); 456 if (parentTree != null) { 457 merged.parent = parentTree; 458 if (isPlusChild) { 459 parentTree.plus = merged; 460 } else { 461 parentTree.minus = merged; 462 } 463 } 464 465 // merging phase 466 plus.merge(merged.plus, leafMerger, merged, true); 467 minus.merge(merged.minus, leafMerger, merged, false); 468 merged.condense(); 469 if (merged.cut != null) { 470 merged.cut = merged.fitToCell(merged.cut.getHyperplane().wholeHyperplane()); 471 } 472 473 return merged; 474 475 } 476 } 477 478 /** This interface gather the merging operations between a BSP tree 479 * leaf and another BSP tree. 480 * <p>As explained in Bruce Naylor, John Amanatides and William 481 * Thibault paper <a 482 * href="http://www.cs.yorku.ca/~amana/research/bsptSetOp.pdf">Merging 483 * BSP Trees Yields Polyhedral Set Operations</a>, 484 * the operations on {@link BSPTree BSP trees} can be expressed as a 485 * generic recursive merging operation where only the final part, 486 * when one of the operand is a leaf, is specific to the real 487 * operation semantics. For example, a tree representing a region 488 * using a boolean attribute to identify inside cells and outside 489 * cells would use four different objects to implement the final 490 * merging phase of the four set operations union, intersection, 491 * difference and symmetric difference (exclusive or).</p> 492 * @param <S> Type of the space. 493 */ 494 public interface LeafMerger<S extends Space> { 495 496 /** Merge a leaf node and a tree node. 497 * <p>This method is called at the end of a recursive merging 498 * resulting from a {@code tree1.merge(tree2, leafMerger)} 499 * call, when one of the sub-trees involved is a leaf (i.e. when 500 * its cut-hyperplane is null). This is the only place where the 501 * precise semantics of the operation are required. For all upper 502 * level nodes in the tree, the merging operation is only a 503 * generic partitioning algorithm.</p> 504 * <p>Since the final operation may be non-commutative, it is 505 * important to know if the leaf node comes from the instance tree 506 * ({@code tree1}) or the argument tree 507 * ({@code tree2}). The third argument of the method is 508 * devoted to this. It can be ignored for commutative 509 * operations.</p> 510 * <p>The {@link BSPTree#insertInTree BSPTree.insertInTree} method 511 * may be useful to implement this method.</p> 512 * @param leaf leaf node (its cut hyperplane is guaranteed to be 513 * null) 514 * @param tree tree node (its cut hyperplane may be null or not) 515 * @param parentTree parent tree to connect to (may be null) 516 * @param isPlusChild if true and if parentTree is not null, the 517 * resulting tree should be the plus child of its parent, ignored if 518 * parentTree is null 519 * @param leafFromInstance if true, the leaf node comes from the 520 * instance tree ({@code tree1}) and the tree node comes from 521 * the argument tree ({@code tree2}) 522 * @return the BSP tree resulting from the merging (may be one of 523 * the arguments) 524 */ 525 BSPTree<S> merge(BSPTree<S> leaf, BSPTree<S> tree, BSPTree<S> parentTree, 526 boolean isPlusChild, boolean leafFromInstance); 527 528 } 529 530 /** This interface handles the corner cases when an internal node cut sub-hyperplane vanishes. 531 * <p> 532 * Such cases happens for example when a cut sub-hyperplane is inserted into 533 * another tree (during a merge operation), and is split in several parts, 534 * some of which becomes smaller than the tolerance. The corresponding node 535 * as then no cut sub-hyperplane anymore, but does have children. This interface 536 * specifies how to handle this situation. 537 * setting 538 * </p> 539 * @param <S> Type of the space. 540 * @since 3.4 541 */ 542 public interface VanishingCutHandler<S extends Space> { 543 544 /** Fix a node with both vanished cut and children. 545 * @param node node to fix 546 * @return fixed node 547 */ 548 BSPTree<S> fixNode(BSPTree<S> node); 549 550 } 551 552 /** Split a BSP tree by an external sub-hyperplane. 553 * <p>Split a tree in two halves, on each side of the 554 * sub-hyperplane. The instance is not modified.</p> 555 * <p>The tree returned is not upward-consistent: despite all of its 556 * sub-trees cut sub-hyperplanes (including its own cut 557 * sub-hyperplane) are bounded to the current cell, it is <em>not</em> 558 * attached to any parent tree yet. This tree is intended to be 559 * later inserted into an higher level tree.</p> 560 * <p>The algorithm used here is the one given in Naylor, Amanatides 561 * and Thibault paper (section III, Binary Partitioning of a BSP 562 * Tree).</p> 563 * @param sub partitioning sub-hyperplane, must be already clipped 564 * to the convex region represented by the instance, will be used as 565 * the cut sub-hyperplane of the returned tree 566 * @return a tree having the specified sub-hyperplane as its cut 567 * sub-hyperplane, the two parts of the split instance as its two 568 * sub-trees and a null parent 569 */ 570 public BSPTree<S> split(final SubHyperplane<S> sub) { 571 572 if (cut == null) { 573 return new BSPTree<S>(sub, copySelf(), new BSPTree<S>(attribute), null); 574 } 575 576 final Hyperplane<S> cHyperplane = cut.getHyperplane(); 577 final Hyperplane<S> sHyperplane = sub.getHyperplane(); 578 final SubHyperplane.SplitSubHyperplane<S> subParts = sub.split(cHyperplane); 579 switch (subParts.getSide()) { 580 case PLUS : 581 { // the partitioning sub-hyperplane is entirely in the plus sub-tree 582 final BSPTree<S> split = plus.split(sub); 583 if (cut.split(sHyperplane).getSide() == Side.PLUS) { 584 split.plus = 585 new BSPTree<S>(cut.copySelf(), split.plus, minus.copySelf(), attribute); 586 split.plus.condense(); 587 split.plus.parent = split; 588 } else { 589 split.minus = 590 new BSPTree<S>(cut.copySelf(), split.minus, minus.copySelf(), attribute); 591 split.minus.condense(); 592 split.minus.parent = split; 593 } 594 return split; 595 } 596 case MINUS : 597 { // the partitioning sub-hyperplane is entirely in the minus sub-tree 598 final BSPTree<S> split = minus.split(sub); 599 if (cut.split(sHyperplane).getSide() == Side.PLUS) { 600 split.plus = 601 new BSPTree<S>(cut.copySelf(), plus.copySelf(), split.plus, attribute); 602 split.plus.condense(); 603 split.plus.parent = split; 604 } else { 605 split.minus = 606 new BSPTree<S>(cut.copySelf(), plus.copySelf(), split.minus, attribute); 607 split.minus.condense(); 608 split.minus.parent = split; 609 } 610 return split; 611 } 612 case BOTH : 613 { 614 final SubHyperplane.SplitSubHyperplane<S> cutParts = cut.split(sHyperplane); 615 final BSPTree<S> split = 616 new BSPTree<S>(sub, plus.split(subParts.getPlus()), minus.split(subParts.getMinus()), 617 null); 618 split.plus.cut = cutParts.getPlus(); 619 split.minus.cut = cutParts.getMinus(); 620 final BSPTree<S> tmp = split.plus.minus; 621 split.plus.minus = split.minus.plus; 622 split.plus.minus.parent = split.plus; 623 split.minus.plus = tmp; 624 split.minus.plus.parent = split.minus; 625 split.plus.condense(); 626 split.minus.condense(); 627 return split; 628 } 629 default : 630 return cHyperplane.sameOrientationAs(sHyperplane) ? 631 new BSPTree<S>(sub, plus.copySelf(), minus.copySelf(), attribute) : 632 new BSPTree<S>(sub, minus.copySelf(), plus.copySelf(), attribute); 633 } 634 635 } 636 637 /** Insert the instance into another tree. 638 * <p>The instance itself is modified so its former parent should 639 * not be used anymore.</p> 640 * @param parentTree parent tree to connect to (may be null) 641 * @param isPlusChild if true and if parentTree is not null, the 642 * resulting tree should be the plus child of its parent, ignored if 643 * parentTree is null 644 * @see LeafMerger 645 * @deprecated as of 3.4, replaced with {@link #insertInTree(BSPTree, boolean, VanishingCutHandler)} 646 */ 647 @Deprecated 648 public void insertInTree(final BSPTree<S> parentTree, final boolean isPlusChild) { 649 insertInTree(parentTree, isPlusChild, new VanishingCutHandler<S>() { 650 /** {@inheritDoc} */ 651 public BSPTree<S> fixNode(BSPTree<S> node) { 652 // the cut should not be null 653 throw new MathIllegalStateException(LocalizedFormats.NULL_NOT_ALLOWED); 654 } 655 }); 656 } 657 658 /** Insert the instance into another tree. 659 * <p>The instance itself is modified so its former parent should 660 * not be used anymore.</p> 661 * @param parentTree parent tree to connect to (may be null) 662 * @param isPlusChild if true and if parentTree is not null, the 663 * resulting tree should be the plus child of its parent, ignored if 664 * parentTree is null 665 * @param vanishingHandler handler to use for handling very rare corner 666 * cases of vanishing cut sub-hyperplanes in internal nodes during merging 667 * @see LeafMerger 668 * @since 3.4 669 */ 670 public void insertInTree(final BSPTree<S> parentTree, final boolean isPlusChild, 671 final VanishingCutHandler<S> vanishingHandler) { 672 673 // set up parent/child links 674 parent = parentTree; 675 if (parentTree != null) { 676 if (isPlusChild) { 677 parentTree.plus = this; 678 } else { 679 parentTree.minus = this; 680 } 681 } 682 683 // make sure the inserted tree lies in the cell defined by its parent nodes 684 if (cut != null) { 685 686 // explore the parent nodes from here towards tree root 687 for (BSPTree<S> tree = this; tree.parent != null; tree = tree.parent) { 688 689 // this is an hyperplane of some parent node 690 final Hyperplane<S> hyperplane = tree.parent.cut.getHyperplane(); 691 692 // chop off the parts of the inserted tree that extend 693 // on the wrong side of this parent hyperplane 694 if (tree == tree.parent.plus) { 695 cut = cut.split(hyperplane).getPlus(); 696 plus.chopOffMinus(hyperplane, vanishingHandler); 697 minus.chopOffMinus(hyperplane, vanishingHandler); 698 } else { 699 cut = cut.split(hyperplane).getMinus(); 700 plus.chopOffPlus(hyperplane, vanishingHandler); 701 minus.chopOffPlus(hyperplane, vanishingHandler); 702 } 703 704 if (cut == null) { 705 // the cut sub-hyperplane has vanished 706 final BSPTree<S> fixed = vanishingHandler.fixNode(this); 707 cut = fixed.cut; 708 plus = fixed.plus; 709 minus = fixed.minus; 710 attribute = fixed.attribute; 711 if (cut == null) { 712 break; 713 } 714 } 715 716 } 717 718 // since we may have drop some parts of the inserted tree, 719 // perform a condensation pass to keep the tree structure simple 720 condense(); 721 722 } 723 724 } 725 726 /** Prune a tree around a cell. 727 * <p> 728 * This method can be used to extract a convex cell from a tree. 729 * The original cell may either be a leaf node or an internal node. 730 * If it is an internal node, it's subtree will be ignored (i.e. the 731 * extracted cell will be a leaf node in all cases). The original 732 * tree to which the original cell belongs is not touched at all, 733 * a new independent tree will be built. 734 * </p> 735 * @param cellAttribute attribute to set for the leaf node 736 * corresponding to the initial instance cell 737 * @param otherLeafsAttributes attribute to set for the other leaf 738 * nodes 739 * @param internalAttributes attribute to set for the internal nodes 740 * @return a new tree (the original tree is left untouched) containing 741 * a single branch with the cell as a leaf node, and other leaf nodes 742 * as the remnants of the pruned branches 743 * @since 3.3 744 */ 745 public BSPTree<S> pruneAroundConvexCell(final Object cellAttribute, 746 final Object otherLeafsAttributes, 747 final Object internalAttributes) { 748 749 // build the current cell leaf 750 BSPTree<S> tree = new BSPTree<S>(cellAttribute); 751 752 // build the pruned tree bottom-up 753 for (BSPTree<S> current = this; current.parent != null; current = current.parent) { 754 final SubHyperplane<S> parentCut = current.parent.cut.copySelf(); 755 final BSPTree<S> sibling = new BSPTree<S>(otherLeafsAttributes); 756 if (current == current.parent.plus) { 757 tree = new BSPTree<S>(parentCut, tree, sibling, internalAttributes); 758 } else { 759 tree = new BSPTree<S>(parentCut, sibling, tree, internalAttributes); 760 } 761 } 762 763 return tree; 764 765 } 766 767 /** Chop off parts of the tree. 768 * <p>The instance is modified in place, all the parts that are on 769 * the minus side of the chopping hyperplane are discarded, only the 770 * parts on the plus side remain.</p> 771 * @param hyperplane chopping hyperplane 772 * @param vanishingHandler handler to use for handling very rare corner 773 * cases of vanishing cut sub-hyperplanes in internal nodes during merging 774 */ 775 private void chopOffMinus(final Hyperplane<S> hyperplane, final VanishingCutHandler<S> vanishingHandler) { 776 if (cut != null) { 777 778 cut = cut.split(hyperplane).getPlus(); 779 plus.chopOffMinus(hyperplane, vanishingHandler); 780 minus.chopOffMinus(hyperplane, vanishingHandler); 781 782 if (cut == null) { 783 // the cut sub-hyperplane has vanished 784 final BSPTree<S> fixed = vanishingHandler.fixNode(this); 785 cut = fixed.cut; 786 plus = fixed.plus; 787 minus = fixed.minus; 788 attribute = fixed.attribute; 789 } 790 791 } 792 } 793 794 /** Chop off parts of the tree. 795 * <p>The instance is modified in place, all the parts that are on 796 * the plus side of the chopping hyperplane are discarded, only the 797 * parts on the minus side remain.</p> 798 * @param hyperplane chopping hyperplane 799 * @param vanishingHandler handler to use for handling very rare corner 800 * cases of vanishing cut sub-hyperplanes in internal nodes during merging 801 */ 802 private void chopOffPlus(final Hyperplane<S> hyperplane, final VanishingCutHandler<S> vanishingHandler) { 803 if (cut != null) { 804 805 cut = cut.split(hyperplane).getMinus(); 806 plus.chopOffPlus(hyperplane, vanishingHandler); 807 minus.chopOffPlus(hyperplane, vanishingHandler); 808 809 if (cut == null) { 810 // the cut sub-hyperplane has vanished 811 final BSPTree<S> fixed = vanishingHandler.fixNode(this); 812 cut = fixed.cut; 813 plus = fixed.plus; 814 minus = fixed.minus; 815 attribute = fixed.attribute; 816 } 817 818 } 819 } 820 821}