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.HashMap;
020import java.util.Map;
021
022import org.apache.commons.math3.geometry.Space;
023
024/** This class implements the dimension-independent parts of {@link SubHyperplane}.
025
026 * <p>sub-hyperplanes are obtained when parts of an {@link
027 * Hyperplane hyperplane} are chopped off by other hyperplanes that
028 * intersect it. The remaining part is a convex region. Such objects
029 * appear in {@link BSPTree BSP trees} as the intersection of a cut
030 * hyperplane with the convex region which it splits, the chopping
031 * hyperplanes are the cut hyperplanes closer to the tree root.</p>
032
033 * @param <S> Type of the embedding space.
034 * @param <T> Type of the embedded sub-space.
035
036 * @since 3.0
037 */
038public abstract class AbstractSubHyperplane<S extends Space, T extends Space>
039    implements SubHyperplane<S> {
040
041    /** Underlying hyperplane. */
042    private final Hyperplane<S> hyperplane;
043
044    /** Remaining region of the hyperplane. */
045    private final Region<T> remainingRegion;
046
047    /** Build a sub-hyperplane from an hyperplane and a region.
048     * @param hyperplane underlying hyperplane
049     * @param remainingRegion remaining region of the hyperplane
050     */
051    protected AbstractSubHyperplane(final Hyperplane<S> hyperplane,
052                                    final Region<T> remainingRegion) {
053        this.hyperplane      = hyperplane;
054        this.remainingRegion = remainingRegion;
055    }
056
057    /** Build a sub-hyperplane from an hyperplane and a region.
058     * @param hyper underlying hyperplane
059     * @param remaining remaining region of the hyperplane
060     * @return a new sub-hyperplane
061     */
062    protected abstract AbstractSubHyperplane<S, T> buildNew(final Hyperplane<S> hyper,
063                                                            final Region<T> remaining);
064
065    /** {@inheritDoc} */
066    public AbstractSubHyperplane<S, T> copySelf() {
067        return buildNew(hyperplane.copySelf(), remainingRegion);
068    }
069
070    /** Get the underlying hyperplane.
071     * @return underlying hyperplane
072     */
073    public Hyperplane<S> getHyperplane() {
074        return hyperplane;
075    }
076
077    /** Get the remaining region of the hyperplane.
078     * <p>The returned region is expressed in the canonical hyperplane
079     * frame and has the hyperplane dimension. For example a chopped
080     * hyperplane in the 3D euclidean is a 2D plane and the
081     * corresponding region is a convex 2D polygon.</p>
082     * @return remaining region of the hyperplane
083     */
084    public Region<T> getRemainingRegion() {
085        return remainingRegion;
086    }
087
088    /** {@inheritDoc} */
089    public double getSize() {
090        return remainingRegion.getSize();
091    }
092
093    /** {@inheritDoc} */
094    public AbstractSubHyperplane<S, T> reunite(final SubHyperplane<S> other) {
095        @SuppressWarnings("unchecked")
096        AbstractSubHyperplane<S, T> o = (AbstractSubHyperplane<S, T>) other;
097        return buildNew(hyperplane,
098                        new RegionFactory<T>().union(remainingRegion, o.remainingRegion));
099    }
100
101    /** Apply a transform to the instance.
102     * <p>The instance must be a (D-1)-dimension sub-hyperplane with
103     * respect to the transform <em>not</em> a (D-2)-dimension
104     * sub-hyperplane the transform knows how to transform by
105     * itself. The transform will consist in transforming first the
106     * hyperplane and then the all region using the various methods
107     * provided by the transform.</p>
108     * @param transform D-dimension transform to apply
109     * @return the transformed instance
110     */
111    public AbstractSubHyperplane<S, T> applyTransform(final Transform<S, T> transform) {
112        final Hyperplane<S> tHyperplane = transform.apply(hyperplane);
113
114        // transform the tree, except for boundary attribute splitters
115        final Map<BSPTree<T>, BSPTree<T>> map = new HashMap<BSPTree<T>, BSPTree<T>>();
116        final BSPTree<T> tTree =
117            recurseTransform(remainingRegion.getTree(false), tHyperplane, transform, map);
118
119        // set up the boundary attributes splitters
120        for (final Map.Entry<BSPTree<T>, BSPTree<T>> entry : map.entrySet()) {
121            if (entry.getKey().getCut() != null) {
122                @SuppressWarnings("unchecked")
123                BoundaryAttribute<T> original = (BoundaryAttribute<T>) entry.getKey().getAttribute();
124                if (original != null) {
125                    @SuppressWarnings("unchecked")
126                    BoundaryAttribute<T> transformed = (BoundaryAttribute<T>) entry.getValue().getAttribute();
127                    for (final BSPTree<T> splitter : original.getSplitters()) {
128                        transformed.getSplitters().add(map.get(splitter));
129                    }
130                }
131            }
132        }
133
134        return buildNew(tHyperplane, remainingRegion.buildNew(tTree));
135
136    }
137
138    /** Recursively transform a BSP-tree from a sub-hyperplane.
139     * @param node current BSP tree node
140     * @param transformed image of the instance hyperplane by the transform
141     * @param transform transform to apply
142     * @param map transformed nodes map
143     * @return a new tree
144     */
145    private BSPTree<T> recurseTransform(final BSPTree<T> node,
146                                        final Hyperplane<S> transformed,
147                                        final Transform<S, T> transform,
148                                        final Map<BSPTree<T>, BSPTree<T>> map) {
149
150        final BSPTree<T> transformedNode;
151        if (node.getCut() == null) {
152            transformedNode = new BSPTree<T>(node.getAttribute());
153        } else {
154
155            @SuppressWarnings("unchecked")
156            BoundaryAttribute<T> attribute = (BoundaryAttribute<T>) node.getAttribute();
157            if (attribute != null) {
158                final SubHyperplane<T> tPO = (attribute.getPlusOutside() == null) ?
159                    null : transform.apply(attribute.getPlusOutside(), hyperplane, transformed);
160                final SubHyperplane<T> tPI = (attribute.getPlusInside() == null) ?
161                    null : transform.apply(attribute.getPlusInside(), hyperplane, transformed);
162                // we start with an empty list of splitters, it will be filled in out of recursion
163                attribute = new BoundaryAttribute<T>(tPO, tPI, new NodesSet<T>());
164            }
165
166            transformedNode = new BSPTree<T>(transform.apply(node.getCut(), hyperplane, transformed),
167                    recurseTransform(node.getPlus(),  transformed, transform, map),
168                    recurseTransform(node.getMinus(), transformed, transform, map),
169                    attribute);
170        }
171
172        map.put(node, transformedNode);
173        return transformedNode;
174
175    }
176
177    /** {@inheritDoc} */
178    @Deprecated
179    public Side side(Hyperplane<S> hyper) {
180        return split(hyper).getSide();
181    }
182
183    /** {@inheritDoc} */
184    public abstract SplitSubHyperplane<S> split(Hyperplane<S> hyper);
185
186    /** {@inheritDoc} */
187    public boolean isEmpty() {
188        return remainingRegion.isEmpty();
189    }
190
191}