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.ArrayList;
    20  import java.util.List;
    21  import java.util.Objects;
    22
    23  import org.apache.commons.geometry.euclidean.internal.AbstractPathConnector;
    24  import org.apache.commons.geometry.euclidean.threed.Vector3D;
    25
    26  /** Abstract class for joining collections of great arcs into connected
    27   * paths. This class is not thread-safe.
    28   */
    29  public abstract class AbstractGreatArcConnector
    30      extends AbstractPathConnector<AbstractGreatArcConnector.ConnectableGreatArc> {
    31      /** Add an arc to the connector, leaving it unconnected until a later call to
    32       * to {@link #connect(Iterable)} or {@link #connectAll()}.
    33       * @param arc arc to add
    34       * @see #connect(Iterable)
    35       * @see #connectAll()
    36       */
    37      public void add(final GreatArc arc) {
    38          addPathElement(new ConnectableGreatArc(arc));
    39      }
    40
    41      /** Add a collection of arcs to the connector, leaving them unconnected
    42       * until a later call to {@link #connect(Iterable)} or
    43       * {@link #connectAll()}.
    44       * @param arcs arcs to add
    45       * @see #connect(Iterable)
    46       * @see #connectAll()
    47       * @see #add(GreatArc)
    48       */
    49      public void add(final Iterable<GreatArc> arcs) {
    50          for (final GreatArc segment : arcs) {
    51              add(segment);
    52          }
    53      }
    54
    55      /** Add a collection of arcs to the connector and attempt to connect each new
    56       * arc with existing ones. Connections made at this time will not be
    57       * overwritten by subsequent calls to this or other connection methods,
    58       * (eg, {@link #connectAll()}).
    59       *
    60       * <p>The connector is not reset by this call. Additional arc can still be added
    61       * to the current set of paths.</p>
    62       * @param arcs arcs to connect
    63       * @see #connectAll()
    64       */
    65      public void connect(final Iterable<GreatArc> arcs) {
    66          final List<ConnectableGreatArc> newEntries = new ArrayList<>();
    67
    68          for (final GreatArc segment : arcs) {
    69              newEntries.add(new ConnectableGreatArc(segment));
    70          }
    71
    72          connectPathElements(newEntries);
    73      }
    74
    75      /** Add the given arcs to this instance and connect all current
    76       * arc into paths. This call is equivalent to
    77       * <pre>
    78       *      connector.add(arcs);
    79       *      List&lt;GreatArcPath&gt; result = connector.connectAll();
    80       * </pre>
    81       *
    82       * <p>The connector is reset after this call. Further calls to
    83       * add or connect arcs will result in new paths being generated.</p>
    84       * @param arcs arcs to add
    85       * @return the connected arc paths
    86       * @see #add(Iterable)
    87       * @see #connectAll()
    88       */
    89      public List<GreatArcPath> connectAll(final Iterable<GreatArc> arcs) {
    90          add(arcs);
    91          return connectAll();
    92      }
    93
    94      /** Connect all current arcs into connected paths, returning the result as a
    95       * list of arc paths.
    96       *
    97       * <p>The connector is reset after this call. Further calls to
    98       * add or connect arcs will result in new paths being generated.</p>
    99       * @return the connected line segments paths
    100      */
    101     public List<GreatArcPath> connectAll() {
    102         final List<ConnectableGreatArc> roots = computePathRoots();
    103         final List<GreatArcPath> paths = new ArrayList<>(roots.size());
    104
    105         for (final ConnectableGreatArc root : roots) {
    106             paths.add(toPath(root));
    107         }
    108
    109         return paths;
    110     }
    111
    112     /** Convert the linked list of path elements starting at the argument
    113      * into a {@link GreatArcPath}.
    114      * @param root root of a connected path linked list
    115      * @return a great arc path representing the linked list path
    116      */
    117     private GreatArcPath toPath(final ConnectableGreatArc root) {
    118         final GreatArcPath.Builder builder = GreatArcPath.builder(null);
    119
    120         builder.append(root.getArc());
    121
    122         ConnectableGreatArc current = root.getNext();
    123
    124         while (current != null && current != root) {
    125             builder.append(current.getArc());
    126             current = current.getNext();
    127         }
    128
    129         return builder.build();
    130     }
    131
    132     /** Internal class for connecting {@link GreatArc}s into {@link GreatArcPath}s.
    133      */
    134     protected static class ConnectableGreatArc extends AbstractPathConnector.ConnectableElement<ConnectableGreatArc> {
    135         /** Segment start point. This will be used to connect to other path elements. */
    136         private final Point2S start;
    137
    138         /** Great arc for this instance. */
    139         private final GreatArc arc;
    140
    141         /** Create a new instance with the given start point. This constructor is
    142          * intended only for performing searches for other path elements.
    143          * @param start start point
    144          */
    145         public ConnectableGreatArc(final Point2S start) {
    146             this(start, null);
    147         }
    148
    149         /** Create a new instance from the given arc.
    150          * @param arc arc for the instance
    151          */
    152         public ConnectableGreatArc(final GreatArc arc) {
    153             this(arc.getStartPoint(), arc);
    154         }
    155
    156         /** Create a new instance with the given start point and arc.
    157          * @param start start point
    158          * @param arc arc for the instance
    159          */
    160         private ConnectableGreatArc(final Point2S start, final GreatArc arc) {
    161             this.start = start;
    162             this.arc = arc;
    163         }
    164
    165         /** Get the arc for the instance.
    166          * @return the arc for the instance
    167          */
    168         public GreatArc getArc() {
    169             return arc;
    170         }
    171
    172         /** {@inheritDoc} */
    173         @Override
    174         public boolean hasStart() {
    175             return start != null;
    176         }
    177
    178         /** {@inheritDoc} */
    179         @Override
    180         public boolean hasEnd() {
    181             return arc.getEndPoint() != null;
    182         }
    183
    184         /** {@inheritDoc} */
    185         @Override
    186         public boolean endPointsEq(final ConnectableGreatArc other) {
    187             if (hasEnd() && other.hasEnd()) {
    188                 return arc.getEndPoint()
    189                         .eq(other.arc.getEndPoint(), arc.getCircle().getPrecision());
    190             }
    191
    192             return false;
    193         }
    194
    195         /** Return true if this instance has a size equivalent to zero.
    196          * @return true if this instance has a size equivalent to zero.
    197          */
    198         public boolean hasZeroSize() {
    199             return arc != null && arc.getCircle().getPrecision().eqZero(arc.getSize());
    200         }
    201
    202         /** {@inheritDoc} */
    203         @Override
    204         public boolean canConnectTo(final ConnectableGreatArc next) {
    205             final Point2S end = arc.getEndPoint();
    206             final Point2S nextStart = next.start;
    207
    208             return end != null && nextStart != null &&
    209                     end.eq(nextStart, arc.getCircle().getPrecision());
    210         }
    211
    212         /** {@inheritDoc} */
    213         @Override
    214         public double getRelativeAngle(final ConnectableGreatArc other) {
    215             return arc.getCircle().angle(other.getArc().getCircle());
    216         }
    217
    218         /** {@inheritDoc} */
    219         @Override
    220         public ConnectableGreatArc getConnectionSearchKey() {
    221             return new ConnectableGreatArc(arc.getEndPoint());
    222         }
    223
    224         /** {@inheritDoc} */
    225         @Override
    226         public boolean shouldContinueConnectionSearch(final ConnectableGreatArc candidate,
    227                 final boolean ascending) {
    228
    229             if (candidate.hasStart()) {
    230                 final double candidatePolar = candidate.getArc().getStartPoint().getPolar();
    231                 final double thisPolar = arc.getEndPoint().getPolar();
    232                 final int cmp = arc.getCircle().getPrecision().compare(candidatePolar, thisPolar);
    233
    234                 return ascending ? cmp <= 0 : cmp >= 0;
    235             }
    236
    237             return true;
    238         }
    239
    240         /** {@inheritDoc} */
    241         @Override
    242         public int compareTo(final ConnectableGreatArc other) {
    243             int cmp = Point2S.POLAR_AZIMUTH_ASCENDING_ORDER.compare(start, other.start);
    244
    245             if (cmp == 0) {
    246                 // sort entries without arcs before ones with arcs
    247                 final boolean thisHasArc = arc != null;
    248                 final boolean otherHasArc = other.arc != null;
    249
    250                 cmp = Boolean.compare(thisHasArc, otherHasArc);
    251
    252                 if (cmp == 0 && thisHasArc) {
    253                     // place point-like segments before ones with non-zero length
    254                     cmp = Boolean.compare(this.hasZeroSize(), other.hasZeroSize());
    255
    256                     if (cmp == 0) {
    257                         // sort by circle pole
    258                         cmp = Vector3D.COORDINATE_ASCENDING_ORDER.compare(
    259                                 arc.getCircle().getPole(),
    260                                 other.arc.getCircle().getPole());
    261                     }
    262                 }
    263             }
    264
    265             return cmp;
    266         }
    267
    268         /** {@inheritDoc} */
    269         @Override
    270         public int hashCode() {
    271             return Objects.hash(start, arc);
    272         }
    273
    274         /** {@inheritDoc} */
    275         @Override
    276         public boolean equals(final Object obj) {
    277             if (this == obj) {
    278                 return true;
    279             }
    280             if (obj == null || !this.getClass().equals(obj.getClass())) {
    281                 return false;
    282             }
    283
    284             final ConnectableGreatArc other = (ConnectableGreatArc) obj;
    285             return Objects.equals(this.start, other.start) &&
    286                     Objects.equals(this.arc, other.arc);
    287         }
    288
    289         /** {@inheritDoc} */
    290         @Override
    291         protected ConnectableGreatArc getSelf() {
    292             return this;
    293         }
    294     }
    295 }