EmbeddedTreeGreatCircleSubset.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.spherical.twod;

  18. import java.util.List;
  19. import java.util.stream.Collectors;

  20. import org.apache.commons.geometry.core.Transform;
  21. import org.apache.commons.geometry.core.partitioning.Hyperplane;
  22. import org.apache.commons.geometry.core.partitioning.Split;
  23. import org.apache.commons.geometry.core.partitioning.SplitLocation;
  24. import org.apache.commons.geometry.spherical.oned.CutAngle;
  25. import org.apache.commons.geometry.spherical.oned.CutAngles;
  26. import org.apache.commons.geometry.spherical.oned.RegionBSPTree1S;

  27. /** Class representing an arbitrary subset of the points on a great circle using a
  28.  * {@link RegionBSPTree1S}. This class can represent convex, non-convex, and empty regions.
  29.  *
  30.  * <p>This class is mutable and <em>not</em> thread safe.</p>
  31.  */
  32. public final class EmbeddedTreeGreatCircleSubset extends GreatCircleSubset {
  33.     /** The 1D region on the great circle. */
  34.     private final RegionBSPTree1S region;

  35.     /** Construct a new, empty hyperplane subset for the given great circle.
  36.      * @param greatCircle great circle defining this instance
  37.      */
  38.     public EmbeddedTreeGreatCircleSubset(final GreatCircle greatCircle) {
  39.         this(greatCircle, false);
  40.     }

  41.     /** Construct a new sub-region for the given great circle. If {@code full}
  42.      * is true, then the region will cover the entire circle; otherwise,
  43.      * it will be empty.
  44.      * @param circle great circle that the sub-region will belong to
  45.      * @param full if true, the sub-region will cover the entire circle;
  46.      *      otherwise it will be empty
  47.      */
  48.     public EmbeddedTreeGreatCircleSubset(final GreatCircle circle, final boolean full) {
  49.         this(circle, new RegionBSPTree1S(full));
  50.     }

  51.     /** Construct a new instance from its defining great circle and subspace region.
  52.      * @param circle great circle that the sub-region will belong to
  53.      * @param region subspace region
  54.      */
  55.     public EmbeddedTreeGreatCircleSubset(final GreatCircle circle, final RegionBSPTree1S region) {
  56.         super(circle);

  57.         this.region = region;
  58.     }

  59.     /** {@inheritDoc} */
  60.     @Override
  61.     public RegionBSPTree1S getSubspaceRegion() {
  62.         return region;
  63.     }

  64.     /** {@inheritDoc} */
  65.     @Override
  66.     public EmbeddedTreeGreatCircleSubset transform(final Transform<Point2S> transform) {
  67.         final GreatCircle circle = getCircle().transform(transform);

  68.         return new EmbeddedTreeGreatCircleSubset(circle, region.copy());
  69.     }

  70.     /** {@inheritDoc} */
  71.     @Override
  72.     public List<GreatArc> toConvex() {
  73.         return region.toIntervals().stream()
  74.                 .flatMap(i -> i.toConvex().stream())
  75.                 .map(i -> GreatCircles.arcFromInterval(getCircle(), i))
  76.                 .collect(Collectors.toList());
  77.     }

  78.     /** {@inheritDoc}
  79.      *
  80.      * <p>In all cases, the current instance is not modified. However, In order to avoid
  81.      * unnecessary copying, this method will use the current instance as the split value when
  82.      * the instance lies entirely on the plus or minus side of the splitter. For example, if
  83.      * this instance lies entirely on the minus side of the splitter, the sub great circle
  84.      * returned by {@link Split#getMinus()} will be this instance. Similarly, {@link Split#getPlus()}
  85.      * will return the current instance if it lies entirely on the plus side. Callers need to make
  86.      * special note of this, since this class is mutable.</p>
  87.      */
  88.     @Override
  89.     public Split<EmbeddedTreeGreatCircleSubset> split(final Hyperplane<Point2S> splitter) {

  90.         final GreatCircle splitterCircle = (GreatCircle) splitter;
  91.         final GreatCircle thisCircle = getCircle();

  92.         final Point2S intersection = splitterCircle.intersection(thisCircle);

  93.         EmbeddedTreeGreatCircleSubset minus = null;
  94.         EmbeddedTreeGreatCircleSubset plus = null;

  95.         if (intersection != null) {
  96.             final CutAngle subSplitter = CutAngles.createPositiveFacing(
  97.                     thisCircle.toSubspace(intersection), splitterCircle.getPrecision());

  98.             final Split<RegionBSPTree1S> subSplit = region.splitDiameter(subSplitter);
  99.             final SplitLocation subLoc = subSplit.getLocation();

  100.             if (subLoc == SplitLocation.MINUS) {
  101.                 minus = this;
  102.             } else if (subLoc == SplitLocation.PLUS) {
  103.                 plus = this;
  104.             } else if (subLoc == SplitLocation.BOTH) {
  105.                 minus = new EmbeddedTreeGreatCircleSubset(thisCircle, subSplit.getMinus());
  106.                 plus =  new EmbeddedTreeGreatCircleSubset(thisCircle, subSplit.getPlus());
  107.             }
  108.         }

  109.         return new Split<>(minus, plus);
  110.     }

  111.     /** Add an arc to this instance.
  112.      * @param arc arc to add
  113.      * @throws IllegalArgumentException if the given arc is not from
  114.      *      a great circle equivalent to this instance
  115.      */
  116.     public void add(final GreatArc arc) {
  117.         GreatCircles.validateGreatCirclesEquivalent(getCircle(), arc.getCircle());

  118.         region.add(arc.getSubspaceRegion());
  119.     }

  120.     /** Add the region represented by the given subcircle to this instance.
  121.      * The argument is not modified.
  122.      * @param subcircle subcircle to add
  123.      * @throws IllegalArgumentException if the given subcircle is not from
  124.      *      a great circle equivalent to this instance
  125.      */
  126.     public void add(final EmbeddedTreeGreatCircleSubset subcircle) {
  127.         GreatCircles.validateGreatCirclesEquivalent(getCircle(), subcircle.getCircle());

  128.         region.union(subcircle.getSubspaceRegion());
  129.     }

  130.     /** {@inheritDoc} */
  131.     @Override
  132.     public String toString() {
  133.         final StringBuilder sb = new StringBuilder();
  134.         sb.append(this.getClass().getSimpleName())
  135.             .append('[')
  136.             .append("circle= ")
  137.             .append(getCircle())
  138.             .append(", region= ")
  139.             .append(region)
  140.             .append(']');

  141.         return sb.toString();
  142.     }
  143. }