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.spherical.twod;
    18
    19  import java.util.List;
    20  import java.util.stream.Collectors;
    21
    22  import org.apache.commons.geometry.core.Transform;
    23  import org.apache.commons.geometry.core.partitioning.Hyperplane;
    24  import org.apache.commons.geometry.core.partitioning.Split;
    25  import org.apache.commons.geometry.core.partitioning.SplitLocation;
    26  import org.apache.commons.geometry.spherical.oned.CutAngle;
    27  import org.apache.commons.geometry.spherical.oned.CutAngles;
    28  import org.apache.commons.geometry.spherical.oned.RegionBSPTree1S;
    29
    30  /** Class representing an arbitrary subset of the points on a great circle using a
    31   * {@link RegionBSPTree1S}. This class can represent convex, non-convex, and empty regions.
    32   *
    33   * <p>This class is mutable and <em>not</em> thread safe.</p>
    34   */
    35  public final class EmbeddedTreeGreatCircleSubset extends GreatCircleSubset {
    36      /** The 1D region on the great circle. */
    37      private final RegionBSPTree1S region;
    38
    39      /** Construct a new, empty hyperplane subset for the given great circle.
    40       * @param greatCircle great circle defining this instance
    41       */
    42      public EmbeddedTreeGreatCircleSubset(final GreatCircle greatCircle) {
    43          this(greatCircle, false);
    44      }
    45
    46      /** Construct a new sub-region for the given great circle. If {@code full}
    47       * is true, then the region will cover the entire circle; otherwise,
    48       * it will be empty.
    49       * @param circle great circle that the sub-region will belong to
    50       * @param full if true, the sub-region will cover the entire circle;
    51       *      otherwise it will be empty
    52       */
    53      public EmbeddedTreeGreatCircleSubset(final GreatCircle circle, final boolean full) {
    54          this(circle, new RegionBSPTree1S(full));
    55      }
    56
    57      /** Construct a new instance from its defining great circle and subspace region.
    58       * @param circle great circle that the sub-region will belong to
    59       * @param region subspace region
    60       */
    61      public EmbeddedTreeGreatCircleSubset(final GreatCircle circle, final RegionBSPTree1S region) {
    62          super(circle);
    63
    64          this.region = region;
    65      }
    66
    67      /** {@inheritDoc} */
    68      @Override
    69      public RegionBSPTree1S getSubspaceRegion() {
    70          return region;
    71      }
    72
    73      /** {@inheritDoc} */
    74      @Override
    75      public EmbeddedTreeGreatCircleSubset transform(final Transform<Point2S> transform) {
    76          final GreatCircle circle = getCircle().transform(transform);
    77
    78          return new EmbeddedTreeGreatCircleSubset(circle, region.copy());
    79      }
    80
    81      /** {@inheritDoc} */
    82      @Override
    83      public List<GreatArc> toConvex() {
    84          return region.toIntervals().stream()
    85                  .flatMap(i -> i.toConvex().stream())
    86                  .map(i -> GreatCircles.arcFromInterval(getCircle(), i))
    87                  .collect(Collectors.toList());
    88      }
    89
    90      /** {@inheritDoc}
    91       *
    92       * <p>In all cases, the current instance is not modified. However, In order to avoid
    93       * unnecessary copying, this method will use the current instance as the split value when
    94       * the instance lies entirely on the plus or minus side of the splitter. For example, if
    95       * this instance lies entirely on the minus side of the splitter, the sub great circle
    96       * returned by {@link Split#getMinus()} will be this instance. Similarly, {@link Split#getPlus()}
    97       * will return the current instance if it lies entirely on the plus side. Callers need to make
    98       * special note of this, since this class is mutable.</p>
    99       */
    100     @Override
    101     public Split<EmbeddedTreeGreatCircleSubset> split(final Hyperplane<Point2S> splitter) {
    102
    103         final GreatCircle splitterCircle = (GreatCircle) splitter;
    104         final GreatCircle thisCircle = getCircle();
    105
    106         final Point2S intersection = splitterCircle.intersection(thisCircle);
    107
    108         EmbeddedTreeGreatCircleSubset minus = null;
    109         EmbeddedTreeGreatCircleSubset plus = null;
    110
    111         if (intersection != null) {
    112             final CutAngle subSplitter = CutAngles.createPositiveFacing(
    113                     thisCircle.toSubspace(intersection), splitterCircle.getPrecision());
    114
    115             final Split<RegionBSPTree1S> subSplit = region.splitDiameter(subSplitter);
    116             final SplitLocation subLoc = subSplit.getLocation();
    117
    118             if (subLoc == SplitLocation.MINUS) {
    119                 minus = this;
    120             } else if (subLoc == SplitLocation.PLUS) {
    121                 plus = this;
    122             } else if (subLoc == SplitLocation.BOTH) {
    123                 minus = new EmbeddedTreeGreatCircleSubset(thisCircle, subSplit.getMinus());
    124                 plus =  new EmbeddedTreeGreatCircleSubset(thisCircle, subSplit.getPlus());
    125             }
    126         }
    127
    128         return new Split<>(minus, plus);
    129     }
    130
    131     /** Add an arc to this instance.
    132      * @param arc arc to add
    133      * @throws IllegalArgumentException if the given arc is not from
    134      *      a great circle equivalent to this instance
    135      */
    136     public void add(final GreatArc arc) {
    137         GreatCircles.validateGreatCirclesEquivalent(getCircle(), arc.getCircle());
    138
    139         region.add(arc.getSubspaceRegion());
    140     }
    141
    142     /** Add the region represented by the given subcircle to this instance.
    143      * The argument is not modified.
    144      * @param subcircle subcircle to add
    145      * @throws IllegalArgumentException if the given subcircle is not from
    146      *      a great circle equivalent to this instance
    147      */
    148     public void add(final EmbeddedTreeGreatCircleSubset subcircle) {
    149         GreatCircles.validateGreatCirclesEquivalent(getCircle(), subcircle.getCircle());
    150
    151         region.union(subcircle.getSubspaceRegion());
    152     }
    153
    154     /** {@inheritDoc} */
    155     @Override
    156     public String toString() {
    157         final StringBuilder sb = new StringBuilder();
    158         sb.append(this.getClass().getSimpleName())
    159             .append('[')
    160             .append("circle= ")
    161             .append(getCircle())
    162             .append(", region= ")
    163             .append(region)
    164             .append(']');
    165
    166         return sb.toString();
    167     }
    168 }