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    *      http://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  package org.apache.commons.geometry.core.partitioning.bsp;
18  
19  import java.util.Collections;
20  import java.util.Iterator;
21  import java.util.List;
22  
23  import org.apache.commons.geometry.core.Point;
24  import org.apache.commons.geometry.core.Sized;
25  import org.apache.commons.geometry.core.partitioning.HyperplaneConvexSubset;
26  
27  /** Class representing the portion of an
28   * {@link AbstractRegionBSPTree.AbstractRegionNode AbstractRegionNode}'s cut that
29   * lies on the boundary of the region. Portions of the node cut may be oriented so
30   * that the plus side of the cut points toward the outside of the region
31   * ({@link #getOutsideFacing()}) and other portions toward the inside of the
32   * region ({@link #getInsideFacing()}). The inside-facing and outside-facing portions
33   * of the region boundary are represented as lists of disjoint hyperplane convex subsets,
34   * all originating from the same hyperplane convex subset forming the node cut.
35   *
36   * @param <P> Point implementation type
37   */
38  public final class RegionCutBoundary<P extends Point<P>> implements Sized {
39  
40      /** Portion of the cut oriented such that the plus side of the cut points to the inside of the region. */
41      private final List<HyperplaneConvexSubset<P>> insideFacing;
42  
43      /** Portion of the cut oriented such that the plus side of the cut points to the outside of the region. */
44      private final List<HyperplaneConvexSubset<P>> outsideFacing;
45  
46      /** Construct a new instance from the inside-facing and outside-facing portions of a node cut. The
47       * given lists are expected to be disjoint regions originating from the same hyperplane convex subset.
48       * No validation is performed.
49       * @param insideFacing the inside-facing portion of the node cut
50       * @param outsideFacing the outside-facing portion of the node cut
51       */
52      RegionCutBoundary(final List<HyperplaneConvexSubset<P>> insideFacing,
53              final List<HyperplaneConvexSubset<P>> outsideFacing) {
54          this.insideFacing = insideFacing != null ?
55                  Collections.unmodifiableList(insideFacing) :
56                  Collections.emptyList();
57  
58          this.outsideFacing = outsideFacing != null ?
59                  Collections.unmodifiableList(outsideFacing) :
60                  Collections.emptyList();
61      }
62  
63      /** Get the portion of the cut with its plus side facing the inside of the region.
64       * @return the portion of the cut with its plus side facing the
65       *      inside of the region
66       */
67      public List<HyperplaneConvexSubset<P>> getInsideFacing() {
68          return insideFacing;
69      }
70  
71      /** Get the portion of the cut with its plus side facing the outside of the region.
72       * @return the portion of the cut with its plus side facing the
73       *      outside of the region
74       */
75      public List<HyperplaneConvexSubset<P>> getOutsideFacing() {
76          return outsideFacing;
77      }
78  
79      /** Get the total size of the cut boundary, including inside and outside facing components.
80       * @return the total size of the cut boundary, including inside and outside facing components
81       */
82      @Override
83      public double getSize() {
84          return getTotalSize(insideFacing) + getTotalSize(outsideFacing);
85      }
86  
87      /** Get the total size of all boundaries in the given list.
88       * @param boundaries boundaries to compute the size for
89       * @return the total size of all boundaries in the given list
90       */
91      private double getTotalSize(final List<? extends HyperplaneConvexSubset<P>> boundaries) {
92          double total = 0.0;
93          for (final HyperplaneConvexSubset<P> boundary : boundaries) {
94              total += boundary.getSize();
95  
96              if (Double.isInfinite(total)) {
97                  return total;
98              }
99          }
100 
101         return total;
102     }
103 
104     /** Return the closest point to the argument in the inside and outside facing
105      * portions of the cut boundary.
106      * @param pt the reference point
107      * @return the point in the cut boundary closest to the reference point
108      * @see HyperplaneConvexSubset#closest(Point)
109      */
110     public P closest(final P pt) {
111         P closest = null;
112         double closestDist = Double.POSITIVE_INFINITY;
113 
114         final Iterator<HyperplaneConvexSubset<P>> insideIt = insideFacing.iterator();
115         final Iterator<HyperplaneConvexSubset<P>> outsideIt = outsideFacing.iterator();
116 
117         HyperplaneConvexSubset<P> boundary;
118         P testPt;
119         double dist;
120 
121         while (insideIt.hasNext() || outsideIt.hasNext()) {
122             boundary = insideIt.hasNext() ?
123                     insideIt.next() :
124                     outsideIt.next();
125 
126             testPt = boundary.closest(pt);
127             dist = pt.distance(testPt);
128 
129             if (closest == null || dist < closestDist) {
130                 closest = testPt;
131                 closestDist = dist;
132             }
133         }
134 
135         return closest;
136     }
137 
138     /** Return true if the given point is contained in the boundary, in either the
139      * inside facing portion or the outside facing portion.
140      * @param pt point to test
141      * @return true if the point is contained in the boundary
142      * @see HyperplaneConvexSubset#contains(Point)
143      */
144     public boolean contains(final P pt) {
145         return containsInsideFacing(pt) || containsOutsideFacing(pt);
146     }
147 
148     /** Return true if the given point is contained in the inside-facing portion of
149      * the region boundary.
150      * @param pt point to test
151      * @return true if the point is contained in the inside-facing portion of the region
152      *      boundary
153      */
154     public boolean containsInsideFacing(final P pt) {
155         return anyContains(pt, insideFacing);
156     }
157 
158     /** Return true if the given point is contained in the outside-facing portion of the
159      * region boundary.
160      * @param pt point to test
161      * @return true if the point is contained in the outside-facing portion of the region
162      *      boundary
163      */
164     public boolean containsOutsideFacing(final P pt) {
165         return anyContains(pt, outsideFacing);
166     }
167 
168     /** Return true if the point is contained in any of the given boundaries.
169      * @param pt point to test
170      * @param boundaries
171      * @return true if the point is contained in any of the given boundaries
172      */
173     private boolean anyContains(final P pt, final List<? extends HyperplaneConvexSubset<P>> boundaries) {
174         for (final HyperplaneConvexSubset<P> boundary : boundaries) {
175             if (boundary.contains(pt)) {
176                 return true;
177             }
178         }
179 
180         return false;
181     }
182 }