blob: 8cfaa46f6d8cf3265892671aea9bf962dbfa3773 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.geometry.core.partitioning.bsp;
import org.apache.commons.geometry.core.Point;
import org.apache.commons.geometry.core.partitioning.bsp.AbstractBSPTree.AbstractNode;
/** Class containing the basic algorithm for merging two {@link AbstractBSPTree}
* instances. Subclasses must override the
* {@link #mergeLeaf(AbstractBSPTree.AbstractNode, AbstractBSPTree.AbstractNode)} method
* to implement the merging logic for their particular use case. The remainder of the
* algorithm is independent of the use case.
*
* <p>This class does not expose any public methods so that subclasses can present their own
* public API, tailored to the specific types being worked with. In particular, most subclasses
* will want to restrict the tree types used with the algorithm, which is difficult to implement
* cleanly at this level.</p>
*
* <p>This class maintains state during the merging process and is therefore
* <em>not</em> thread-safe.</p>
* @param <P> Point implementation type
* @param <N> BSP tree node implementation type
*/
public abstract class AbstractBSPTreeMergeOperator<P extends Point<P>, N extends AbstractNode<P, N>> {
/** The tree that the merge operation output will be written to. All existing content
* in this tree is overwritten.
*/
private AbstractBSPTree<P, N> outputTree;
/** Set the tree used as output for this instance.
* @param outputTree the tree used as output for this instance
*/
protected void setOutputTree(final AbstractBSPTree<P, N> outputTree) {
this.outputTree = outputTree;
}
/** Get the tree used as output for this instance.
* @return the tree used as output for this instance
*/
protected AbstractBSPTree<P, N> getOutputTree() {
return outputTree;
}
/** Perform a merge operation with the two input trees and store the result in the output tree. The
* output tree may be one of the input trees, in which case, the tree is modified in place.
* @param input1 first input tree
* @param input2 second input tree
* @param output output tree all previous content in this tree is overwritten
*/
protected void performMerge(final AbstractBSPTree<P, N> input1, final AbstractBSPTree<P, N> input2,
final AbstractBSPTree<P, N> output) {
setOutputTree(output);
final N root1 = input1.getRoot();
final N root2 = input2.getRoot();
final N outputRoot = performMergeRecursive(root1, root2);
getOutputTree().setRoot(outputRoot);
}
/** Recursively merge two nodes.
* @param node1 node from the first input tree
* @param node2 node from the second input tree
* @return a merged node
*/
private N performMergeRecursive(final N node1, final N node2) {
if (node1.isLeaf() || node2.isLeaf()) {
// delegate to the mergeLeaf method if we can no longer continue
// merging recursively
final N merged = mergeLeaf(node1, node2);
// copy the merged node to the output if needed (in case mergeLeaf
// returned one of the input nodes directly)
return outputTree.importSubtree(merged);
} else {
final N partitioned = outputTree.splitSubtree(node2, node1.getCut());
final N minus = performMergeRecursive(node1.getMinus(), partitioned.getMinus());
final N plus = performMergeRecursive(node1.getPlus(), partitioned.getPlus());
final N outputNode = outputTree.copyNode(node1);
outputNode.setSubtree(node1.getCut(), minus, plus);
return outputNode;
}
}
/** Create a new node in the output tree. The node is associated with the output tree but
* is not attached to a parent node.
* @return a new node associated with the output tree but not yet attached to a parent
*/
protected N outputNode() {
return outputTree.createNode();
}
/** Create a new node in the output tree with the same non-structural properties as the given
* node. Non-structural properties are properties other than parent, children, or cut. The
* returned node is associated with the output tree but is not attached to a parent node.
* Note that this method only copies the given node and <strong>not</strong> any of its children.
* @param node the input node to copy properties from
* @return a new node in the output tree
*/
protected N outputNode(final N node) {
return outputTree.copyNode(node);
}
/** Place the subtree rooted at the given input node into the output tree. The subtree
* is copied if needed.
* @param node the root of the subtree to copy
* @return a subtree in the output tree
*/
protected N outputSubtree(final N node) {
return outputTree.importSubtree(node);
}
/** Merge a leaf node from one input with a subtree from another.
* <p>When this method is called, one or both of the given nodes will be a leaf node.
* This method is expected to return a node representing the merger of the two given
* nodes. The way that the returned node is determined defines the overall behavior of
* the merge operation.
* </p>
* <p>The return value can be one of the two input nodes or a completely different one.</p>
* @param node1 node from the first input tree
* @param node2 node from the second input tree
* @return node representing the merger of the two input nodes
*/
protected abstract N mergeLeaf(N node1, N node2);
}