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 }