RegionCutBoundary.java

  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. import java.util.Collections;
  19. import java.util.Iterator;
  20. import java.util.List;

  21. import org.apache.commons.geometry.core.Point;
  22. import org.apache.commons.geometry.core.Sized;
  23. import org.apache.commons.geometry.core.partitioning.HyperplaneConvexSubset;

  24. /** Class representing the portion of an
  25.  * {@link AbstractRegionBSPTree.AbstractRegionNode AbstractRegionNode}'s cut that
  26.  * lies on the boundary of the region. Portions of the node cut may be oriented so
  27.  * that the plus side of the cut points toward the outside of the region
  28.  * ({@link #getOutsideFacing()}) and other portions toward the inside of the
  29.  * region ({@link #getInsideFacing()}). The inside-facing and outside-facing portions
  30.  * of the region boundary are represented as lists of disjoint hyperplane convex subsets,
  31.  * all originating from the same hyperplane convex subset forming the node cut.
  32.  *
  33.  * @param <P> Point implementation type
  34.  */
  35. public final class RegionCutBoundary<P extends Point<P>> implements Sized {

  36.     /** Portion of the cut oriented such that the plus side of the cut points to the inside of the region. */
  37.     private final List<HyperplaneConvexSubset<P>> insideFacing;

  38.     /** Portion of the cut oriented such that the plus side of the cut points to the outside of the region. */
  39.     private final List<HyperplaneConvexSubset<P>> outsideFacing;

  40.     /** Construct a new instance from the inside-facing and outside-facing portions of a node cut. The
  41.      * given lists are expected to be disjoint regions originating from the same hyperplane convex subset.
  42.      * No validation is performed.
  43.      * @param insideFacing the inside-facing portion of the node cut
  44.      * @param outsideFacing the outside-facing portion of the node cut
  45.      */
  46.     RegionCutBoundary(final List<HyperplaneConvexSubset<P>> insideFacing,
  47.             final List<HyperplaneConvexSubset<P>> outsideFacing) {
  48.         this.insideFacing = insideFacing != null ?
  49.                 Collections.unmodifiableList(insideFacing) :
  50.                 Collections.emptyList();

  51.         this.outsideFacing = outsideFacing != null ?
  52.                 Collections.unmodifiableList(outsideFacing) :
  53.                 Collections.emptyList();
  54.     }

  55.     /** Get the portion of the cut with its plus side facing the inside of the region.
  56.      * @return the portion of the cut with its plus side facing the
  57.      *      inside of the region
  58.      */
  59.     public List<HyperplaneConvexSubset<P>> getInsideFacing() {
  60.         return insideFacing;
  61.     }

  62.     /** Get the portion of the cut with its plus side facing the outside of the region.
  63.      * @return the portion of the cut with its plus side facing the
  64.      *      outside of the region
  65.      */
  66.     public List<HyperplaneConvexSubset<P>> getOutsideFacing() {
  67.         return outsideFacing;
  68.     }

  69.     /** Get the total size of the cut boundary, including inside and outside facing components.
  70.      * @return the total size of the cut boundary, including inside and outside facing components
  71.      */
  72.     @Override
  73.     public double getSize() {
  74.         return getTotalSize(insideFacing) + getTotalSize(outsideFacing);
  75.     }

  76.     /** Get the total size of all boundaries in the given list.
  77.      * @param boundaries boundaries to compute the size for
  78.      * @return the total size of all boundaries in the given list
  79.      */
  80.     private double getTotalSize(final List<? extends HyperplaneConvexSubset<P>> boundaries) {
  81.         double total = 0.0;
  82.         for (final HyperplaneConvexSubset<P> boundary : boundaries) {
  83.             total += boundary.getSize();

  84.             if (Double.isInfinite(total)) {
  85.                 return total;
  86.             }
  87.         }

  88.         return total;
  89.     }

  90.     /** Return the closest point to the argument in the inside and outside facing
  91.      * portions of the cut boundary.
  92.      * @param pt the reference point
  93.      * @return the point in the cut boundary closest to the reference point
  94.      * @see HyperplaneConvexSubset#closest(Point)
  95.      */
  96.     public P closest(final P pt) {
  97.         P closest = null;
  98.         double closestDist = Double.POSITIVE_INFINITY;

  99.         final Iterator<HyperplaneConvexSubset<P>> insideIt = insideFacing.iterator();
  100.         final Iterator<HyperplaneConvexSubset<P>> outsideIt = outsideFacing.iterator();

  101.         HyperplaneConvexSubset<P> boundary;
  102.         P testPt;
  103.         double dist;

  104.         while (insideIt.hasNext() || outsideIt.hasNext()) {
  105.             boundary = insideIt.hasNext() ?
  106.                     insideIt.next() :
  107.                     outsideIt.next();

  108.             testPt = boundary.closest(pt);
  109.             dist = pt.distance(testPt);

  110.             if (closest == null || dist < closestDist) {
  111.                 closest = testPt;
  112.                 closestDist = dist;
  113.             }
  114.         }

  115.         return closest;
  116.     }

  117.     /** Return true if the given point is contained in the boundary, in either the
  118.      * inside facing portion or the outside facing portion.
  119.      * @param pt point to test
  120.      * @return true if the point is contained in the boundary
  121.      * @see HyperplaneConvexSubset#contains(Point)
  122.      */
  123.     public boolean contains(final P pt) {
  124.         return containsInsideFacing(pt) || containsOutsideFacing(pt);
  125.     }

  126.     /** Return true if the given point is contained in the inside-facing portion of
  127.      * the region boundary.
  128.      * @param pt point to test
  129.      * @return true if the point is contained in the inside-facing portion of the region
  130.      *      boundary
  131.      */
  132.     public boolean containsInsideFacing(final P pt) {
  133.         return anyContains(pt, insideFacing);
  134.     }

  135.     /** Return true if the given point is contained in the outside-facing portion of the
  136.      * region boundary.
  137.      * @param pt point to test
  138.      * @return true if the point is contained in the outside-facing portion of the region
  139.      *      boundary
  140.      */
  141.     public boolean containsOutsideFacing(final P pt) {
  142.         return anyContains(pt, outsideFacing);
  143.     }

  144.     /** Return true if the point is contained in any of the given boundaries.
  145.      * @param pt point to test
  146.      * @param boundaries
  147.      * @return true if the point is contained in any of the given boundaries
  148.      */
  149.     private boolean anyContains(final P pt, final List<? extends HyperplaneConvexSubset<P>> boundaries) {
  150.         for (final HyperplaneConvexSubset<P> boundary : boundaries) {
  151.             if (boundary.contains(pt)) {
  152.                 return true;
  153.             }
  154.         }

  155.         return false;
  156.     }
  157. }