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 */
017 package org.apache.commons.math3.geometry.partitioning;
018
019 import org.apache.commons.math3.geometry.Space;
020
021 /** This class implements the dimension-independent parts of {@link SubHyperplane}.
022
023 * <p>sub-hyperplanes are obtained when parts of an {@link
024 * Hyperplane hyperplane} are chopped off by other hyperplanes that
025 * intersect it. The remaining part is a convex region. Such objects
026 * appear in {@link BSPTree BSP trees} as the intersection of a cut
027 * hyperplane with the convex region which it splits, the chopping
028 * hyperplanes are the cut hyperplanes closer to the tree root.</p>
029
030 * @param <S> Type of the embedding space.
031 * @param <T> Type of the embedded sub-space.
032
033 * @version $Id: AbstractSubHyperplane.java 1421448 2012-12-13 19:45:57Z tn $
034 * @since 3.0
035 */
036 public abstract class AbstractSubHyperplane<S extends Space, T extends Space>
037 implements SubHyperplane<S> {
038
039 /** Underlying hyperplane. */
040 private final Hyperplane<S> hyperplane;
041
042 /** Remaining region of the hyperplane. */
043 private final Region<T> remainingRegion;
044
045 /** Build a sub-hyperplane from an hyperplane and a region.
046 * @param hyperplane underlying hyperplane
047 * @param remainingRegion remaining region of the hyperplane
048 */
049 protected AbstractSubHyperplane(final Hyperplane<S> hyperplane,
050 final Region<T> remainingRegion) {
051 this.hyperplane = hyperplane;
052 this.remainingRegion = remainingRegion;
053 }
054
055 /** Build a sub-hyperplane from an hyperplane and a region.
056 * @param hyper underlying hyperplane
057 * @param remaining remaining region of the hyperplane
058 * @return a new sub-hyperplane
059 */
060 protected abstract AbstractSubHyperplane<S, T> buildNew(final Hyperplane<S> hyper,
061 final Region<T> remaining);
062
063 /** {@inheritDoc} */
064 public AbstractSubHyperplane<S, T> copySelf() {
065 return buildNew(hyperplane, remainingRegion);
066 }
067
068 /** Get the underlying hyperplane.
069 * @return underlying hyperplane
070 */
071 public Hyperplane<S> getHyperplane() {
072 return hyperplane;
073 }
074
075 /** Get the remaining region of the hyperplane.
076 * <p>The returned region is expressed in the canonical hyperplane
077 * frame and has the hyperplane dimension. For example a chopped
078 * hyperplane in the 3D euclidean is a 2D plane and the
079 * corresponding region is a convex 2D polygon.</p>
080 * @return remaining region of the hyperplane
081 */
082 public Region<T> getRemainingRegion() {
083 return remainingRegion;
084 }
085
086 /** {@inheritDoc} */
087 public double getSize() {
088 return remainingRegion.getSize();
089 }
090
091 /** {@inheritDoc} */
092 public AbstractSubHyperplane<S, T> reunite(final SubHyperplane<S> other) {
093 @SuppressWarnings("unchecked")
094 AbstractSubHyperplane<S, T> o = (AbstractSubHyperplane<S, T>) other;
095 return buildNew(hyperplane,
096 new RegionFactory<T>().union(remainingRegion, o.remainingRegion));
097 }
098
099 /** Apply a transform to the instance.
100 * <p>The instance must be a (D-1)-dimension sub-hyperplane with
101 * respect to the transform <em>not</em> a (D-2)-dimension
102 * sub-hyperplane the transform knows how to transform by
103 * itself. The transform will consist in transforming first the
104 * hyperplane and then the all region using the various methods
105 * provided by the transform.</p>
106 * @param transform D-dimension transform to apply
107 * @return the transformed instance
108 */
109 public AbstractSubHyperplane<S, T> applyTransform(final Transform<S, T> transform) {
110 final Hyperplane<S> tHyperplane = transform.apply(hyperplane);
111 final BSPTree<T> tTree =
112 recurseTransform(remainingRegion.getTree(false), tHyperplane, transform);
113 return buildNew(tHyperplane, remainingRegion.buildNew(tTree));
114 }
115
116 /** Recursively transform a BSP-tree from a sub-hyperplane.
117 * @param node current BSP tree node
118 * @param transformed image of the instance hyperplane by the transform
119 * @param transform transform to apply
120 * @return a new tree
121 */
122 private BSPTree<T> recurseTransform(final BSPTree<T> node,
123 final Hyperplane<S> transformed,
124 final Transform<S, T> transform) {
125 if (node.getCut() == null) {
126 return new BSPTree<T>(node.getAttribute());
127 }
128
129 @SuppressWarnings("unchecked")
130 BoundaryAttribute<T> attribute =
131 (BoundaryAttribute<T>) node.getAttribute();
132 if (attribute != null) {
133 final SubHyperplane<T> tPO = (attribute.getPlusOutside() == null) ?
134 null : transform.apply(attribute.getPlusOutside(), hyperplane, transformed);
135 final SubHyperplane<T> tPI = (attribute.getPlusInside() == null) ?
136 null : transform.apply(attribute.getPlusInside(), hyperplane, transformed);
137 attribute = new BoundaryAttribute<T>(tPO, tPI);
138 }
139
140 return new BSPTree<T>(transform.apply(node.getCut(), hyperplane, transformed),
141 recurseTransform(node.getPlus(), transformed, transform),
142 recurseTransform(node.getMinus(), transformed, transform),
143 attribute);
144
145 }
146
147 /** {@inheritDoc} */
148 public abstract Side side(Hyperplane<S> hyper);
149
150 /** {@inheritDoc} */
151 public abstract SplitSubHyperplane<S> split(Hyperplane<S> hyper);
152
153 /** {@inheritDoc} */
154 public boolean isEmpty() {
155 return remainingRegion.isEmpty();
156 }
157
158 }