View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  /*
19   * This is not the original file distributed by the Apache Software Foundation
20   * It has been modified by the Hipparchus project
21   */
22  package org.hipparchus.geometry.partitioning;
23  
24  import java.util.HashMap;
25  import java.util.Map;
26  
27  import org.hipparchus.geometry.Space;
28  
29  /** This class implements the dimension-independent parts of {@link SubHyperplane}.
30  
31   * <p>sub-hyperplanes are obtained when parts of an {@link
32   * Hyperplane hyperplane} are chopped off by other hyperplanes that
33   * intersect it. The remaining part is a convex region. Such objects
34   * appear in {@link BSPTree BSP trees} as the intersection of a cut
35   * hyperplane with the convex region which it splits, the chopping
36   * hyperplanes are the cut hyperplanes closer to the tree root.</p>
37  
38   * @param <S> Type of the embedding space.
39   * @param <T> Type of the embedded sub-space.
40  
41   */
42  public abstract class AbstractSubHyperplane<S extends Space, T extends Space>
43      implements SubHyperplane<S> {
44  
45      /** Underlying hyperplane. */
46      private final Hyperplane<S> hyperplane;
47  
48      /** Remaining region of the hyperplane. */
49      private final Region<T> remainingRegion;
50  
51      /** Build a sub-hyperplane from an hyperplane and a region.
52       * @param hyperplane underlying hyperplane
53       * @param remainingRegion remaining region of the hyperplane
54       */
55      protected AbstractSubHyperplane(final Hyperplane<S> hyperplane,
56                                      final Region<T> remainingRegion) {
57          this.hyperplane      = hyperplane;
58          this.remainingRegion = remainingRegion;
59      }
60  
61      /** Build a sub-hyperplane from an hyperplane and a region.
62       * @param hyper underlying hyperplane
63       * @param remaining remaining region of the hyperplane
64       * @return a new sub-hyperplane
65       */
66      protected abstract AbstractSubHyperplane<S, T> buildNew(Hyperplane<S> hyper,
67                                                              Region<T> remaining);
68  
69      /** {@inheritDoc} */
70      @Override
71      public AbstractSubHyperplane<S, T> copySelf() {
72          return buildNew(hyperplane.copySelf(), remainingRegion);
73      }
74  
75      /** Get the underlying hyperplane.
76       * @return underlying hyperplane
77       */
78      @Override
79      public Hyperplane<S> getHyperplane() {
80          return hyperplane;
81      }
82  
83      /** Get the remaining region of the hyperplane.
84       * <p>The returned region is expressed in the canonical hyperplane
85       * frame and has the hyperplane dimension. For example a chopped
86       * hyperplane in the 3D euclidean is a 2D plane and the
87       * corresponding region is a convex 2D polygon.</p>
88       * @return remaining region of the hyperplane
89       */
90      public Region<T> getRemainingRegion() {
91          return remainingRegion;
92      }
93  
94      /** {@inheritDoc} */
95      @Override
96      public double getSize() {
97          return remainingRegion.getSize();
98      }
99  
100     /** {@inheritDoc} */
101     @Override
102     public AbstractSubHyperplane<S, T> reunite(final SubHyperplane<S> other) {
103         @SuppressWarnings("unchecked")
104         AbstractSubHyperplane<S, T> o = (AbstractSubHyperplane<S, T>) other;
105         return buildNew(hyperplane,
106                         new RegionFactory<T>().union(remainingRegion, o.remainingRegion));
107     }
108 
109     /** Apply a transform to the instance.
110      * <p>The instance must be a (D-1)-dimension sub-hyperplane with
111      * respect to the transform <em>not</em> a (D-2)-dimension
112      * sub-hyperplane the transform knows how to transform by
113      * itself. The transform will consist in transforming first the
114      * hyperplane and then the all region using the various methods
115      * provided by the transform.</p>
116      * @param transform D-dimension transform to apply
117      * @return the transformed instance
118      */
119     public AbstractSubHyperplane<S, T> applyTransform(final Transform<S, T> transform) {
120         final Hyperplane<S> tHyperplane = transform.apply(hyperplane);
121 
122         // transform the tree, except for boundary attribute splitters
123         final Map<BSPTree<T>, BSPTree<T>> map = new HashMap<>();
124         final BSPTree<T> tTree =
125             recurseTransform(remainingRegion.getTree(false), tHyperplane, transform, map);
126 
127         // set up the boundary attributes splitters
128         for (final Map.Entry<BSPTree<T>, BSPTree<T>> entry : map.entrySet()) {
129             if (entry.getKey().getCut() != null) {
130                 @SuppressWarnings("unchecked")
131                 BoundaryAttribute<T> original = (BoundaryAttribute<T>) entry.getKey().getAttribute();
132                 if (original != null) {
133                     @SuppressWarnings("unchecked")
134                     BoundaryAttribute<T> transformed = (BoundaryAttribute<T>) entry.getValue().getAttribute();
135                     for (final BSPTree<T> splitter : original.getSplitters()) {
136                         transformed.getSplitters().add(map.get(splitter));
137                     }
138                 }
139             }
140         }
141 
142         return buildNew(tHyperplane, remainingRegion.buildNew(tTree));
143 
144     }
145 
146     /** Recursively transform a BSP-tree from a sub-hyperplane.
147      * @param node current BSP tree node
148      * @param transformed image of the instance hyperplane by the transform
149      * @param transform transform to apply
150      * @param map transformed nodes map
151      * @return a new tree
152      */
153     private BSPTree<T> recurseTransform(final BSPTree<T> node,
154                                         final Hyperplane<S> transformed,
155                                         final Transform<S, T> transform,
156                                         final Map<BSPTree<T>, BSPTree<T>> map) {
157 
158         final BSPTree<T> transformedNode;
159         if (node.getCut() == null) {
160             transformedNode = new BSPTree<>(node.getAttribute());
161         } else {
162 
163             @SuppressWarnings("unchecked")
164             BoundaryAttribute<T> attribute = (BoundaryAttribute<T>) node.getAttribute();
165             if (attribute != null) {
166                 final SubHyperplane<T> tPO = (attribute.getPlusOutside() == null) ?
167                     null : transform.apply(attribute.getPlusOutside(), hyperplane, transformed);
168                 final SubHyperplane<T> tPI = (attribute.getPlusInside() == null) ?
169                     null : transform.apply(attribute.getPlusInside(), hyperplane, transformed);
170                 // we start with an empty list of splitters, it will be filled in out of recursion
171                 attribute = new BoundaryAttribute<>(tPO, tPI, new NodesSet<>());
172             }
173 
174             transformedNode = new BSPTree<>(transform.apply(node.getCut(), hyperplane, transformed),
175                                             recurseTransform(node.getPlus(),  transformed, transform, map),
176                                             recurseTransform(node.getMinus(), transformed, transform, map),
177                                             attribute);
178         }
179 
180         map.put(node, transformedNode);
181         return transformedNode;
182 
183     }
184 
185     /** {@inheritDoc} */
186     @Override
187     public abstract SplitSubHyperplane<S> split(Hyperplane<S> hyper);
188 
189     /** {@inheritDoc} */
190     @Override
191     public boolean isEmpty() {
192         return remainingRegion.isEmpty();
193     }
194 
195 }