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.geometry.core.partitioning.bsp;
018
019import org.apache.commons.geometry.core.Point;
020import org.apache.commons.geometry.core.partitioning.bsp.AbstractBSPTree.AbstractNode;
021
022/** Class containing the basic algorithm for merging two {@link AbstractBSPTree}
023 * instances. Subclasses must override the
024 * {@link #mergeLeaf(AbstractBSPTree.AbstractNode, AbstractBSPTree.AbstractNode)} method
025 * to implement the merging logic for their particular use case. The remainder of the
026 * algorithm is independent of the use case.
027 *
028 * <p>This class does not expose any public methods so that subclasses can present their own
029 * public API, tailored to the specific types being worked with. In particular, most subclasses
030 * will want to restrict the tree types used with the algorithm, which is difficult to implement
031 * cleanly at this level.</p>
032 *
033 * <p>This class maintains state during the merging process and is therefore
034 * <em>not</em> thread-safe.</p>
035 * @param <P> Point implementation type
036 * @param <N> BSP tree node implementation type
037 */
038public abstract class AbstractBSPTreeMergeOperator<P extends Point<P>, N extends AbstractNode<P, N>> {
039
040    /** The tree that the merge operation output will be written to. All existing content
041     * in this tree is overwritten.
042     */
043    private AbstractBSPTree<P, N> outputTree;
044
045    /** Set the tree used as output for this instance.
046     * @param outputTree the tree used as output for this instance
047     */
048    protected void setOutputTree(final AbstractBSPTree<P, N> outputTree) {
049        this.outputTree = outputTree;
050    }
051
052    /** Get the tree used as output for this instance.
053     * @return the tree used as output for this instance
054     */
055    protected AbstractBSPTree<P, N> getOutputTree() {
056        return outputTree;
057    }
058
059    /** Perform a merge operation with the two input trees and store the result in the output tree. The
060     * output tree may be one of the input trees, in which case, the tree is modified in place.
061     * @param input1 first input tree
062     * @param input2 second input tree
063     * @param output output tree all previous content in this tree is overwritten
064     */
065    protected void performMerge(final AbstractBSPTree<P, N> input1, final AbstractBSPTree<P, N> input2,
066            final AbstractBSPTree<P, N> output) {
067
068        setOutputTree(output);
069
070        final N root1 = input1.getRoot();
071        final N root2 = input2.getRoot();
072
073        final N outputRoot = performMergeRecursive(root1, root2);
074
075        getOutputTree().setRoot(outputRoot);
076    }
077
078    /** Recursively merge two nodes.
079     * @param node1 node from the first input tree
080     * @param node2 node from the second input tree
081     * @return a merged node
082     */
083    private N performMergeRecursive(final N node1, final N node2) {
084
085        if (node1.isLeaf() || node2.isLeaf()) {
086            // delegate to the mergeLeaf method if we can no longer continue
087            // merging recursively
088            final N merged = mergeLeaf(node1, node2);
089
090            // copy the merged node to the output if needed (in case mergeLeaf
091            // returned one of the input nodes directly)
092            return outputTree.importSubtree(merged);
093        } else {
094            final N partitioned = outputTree.splitSubtree(node2, node1.getCut());
095
096            final N minus = performMergeRecursive(node1.getMinus(), partitioned.getMinus());
097
098            final N plus = performMergeRecursive(node1.getPlus(), partitioned.getPlus());
099
100            final N outputNode = outputTree.copyNode(node1);
101            outputNode.setSubtree(node1.getCut(), minus, plus);
102
103            return outputNode;
104        }
105    }
106
107    /** Create a new node in the output tree. The node is associated with the output tree but
108     * is not attached to a parent node.
109     * @return a new node associated with the output tree but not yet attached to a parent
110     */
111    protected N outputNode() {
112        return outputTree.createNode();
113    }
114
115    /** Place the subtree rooted at the given input node into the output tree. The subtree
116     * is copied if needed.
117     * @param node the root of the subtree to copy
118     * @return a subtree in the output tree
119     */
120    protected N outputSubtree(final N node) {
121        return outputTree.importSubtree(node);
122    }
123
124    /** Merge a leaf node from one input with a subtree from another.
125     * <p>When this method is called, one or both of the given nodes will be a leaf node.
126     * This method is expected to return a node representing the merger of the two given
127     * nodes. The way that the returned node is determined defines the overall behavior of
128     * the merge operation.
129     * </p>
130     * <p>The return value can be one of the two input nodes or a completely different one.</p>
131     * @param node1 node from the first input tree
132     * @param node2 node from the second input tree
133     * @return node representing the merger of the two input nodes
134     */
135    protected abstract N mergeLeaf(N node1, N node2);
136}