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.Collection;
20  import java.util.List;
21  
22  import org.apache.commons.numbers.angle.PlaneAngleRadians;
23  
24  /** Great arc connector that selects between multiple connection options
25   * based on the resulting interior angle. An interior angle in this
26   * case is the angle created between an incoming arc and an outgoing arc
27   * as measured on the minus (interior) side of the incoming arc. If looking
28   * along the direction of the incoming arc, smaller interior angles
29   * point more to the left and larger ones point more to the right.
30   *
31   * <p>This class provides two concrete implementations: {@link Maximize} and
32   * {@link Minimize}, which choose connections with the largest or smallest interior
33   * angles respectively.
34   * </p>
35   */
36  public abstract class InteriorAngleGreatArcConnector extends AbstractGreatArcConnector {
37      /** {@inheritDoc} */
38      @Override
39      protected ConnectableGreatArc selectConnection(final ConnectableGreatArc incoming,
40              final List<ConnectableGreatArc> outgoing) {
41  
42          // search for the best connection
43          final GreatCircle circle = incoming.getArc().getCircle();
44  
45          double selectedInteriorAngle = Double.POSITIVE_INFINITY;
46          ConnectableGreatArc selected = null;
47  
48          for (final ConnectableGreatArc candidate : outgoing) {
49              final double interiorAngle = PlaneAngleRadians.PI - circle.angle(candidate.getArc().getCircle(),
50                      incoming.getArc().getEndPoint());
51  
52              if (selected == null || isBetterAngle(interiorAngle, selectedInteriorAngle)) {
53                  selectedInteriorAngle = interiorAngle;
54                  selected = candidate;
55              }
56          }
57  
58          return selected;
59      }
60  
61      /** Return true if {@code newAngle} represents a better interior angle than {@code previousAngle}.
62       * @param newAngle the new angle under consideration
63       * @param previousAngle the previous best angle
64       * @return true if {@code newAngle} represents a better interior angle than {@code previousAngle}
65       */
66      protected abstract boolean isBetterAngle(double newAngle, double previousAngle);
67  
68      /** Convenience method for connecting a set of arcs with interior angles maximized
69       * when possible. This method is equivalent to {@code new Maximize().connect(segments)}.
70       * @param arcs arcs to connect
71       * @return a list of connected arc paths
72       * @see Maximize
73       */
74      public static List<GreatArcPath> connectMaximized(final Collection<GreatArc> arcs) {
75          return new Maximize().connectAll(arcs);
76      }
77  
78      /** Convenience method for connecting a set of line segments with interior angles minimized
79       * when possible. This method is equivalent to {@code new Minimize().connect(segments)}.
80       * @param arcs arcs to connect
81       * @return a list of connected arc paths
82       * @see Minimize
83       */
84      public static List<GreatArcPath> connectMinimized(final Collection<GreatArc> arcs) {
85          return new Minimize().connectAll(arcs);
86      }
87  
88      /** Implementation of {@link InteriorAngleGreatArcConnector} that chooses arc
89       * connections that produce the largest interior angles. Another way to visualize this is
90       * that when presented multiple connection options for a given arc, this class will
91       * choose the option that points most to the right when viewed in the direction of the incoming
92       * arc.
93       */
94      public static class Maximize extends InteriorAngleGreatArcConnector {
95          /** {@inheritDoc} */
96          @Override
97          protected boolean isBetterAngle(final double newAngle, final double previousAngle) {
98              return newAngle > previousAngle;
99          }
100     }
101 
102     /** Implementation of {@link InteriorAngleGreatArcConnector} that chooses arc
103      * connections that produce the smallest interior angles. Another way to visualize this is
104      * that when presented multiple connection options for a given arc, this class will
105      * choose the option that points most to the left when viewed in the direction of the incoming
106      * arc.
107      */
108     public static class Minimize extends InteriorAngleGreatArcConnector {
109         /** {@inheritDoc} */
110         @Override
111         protected boolean isBetterAngle(final double newAngle, final double previousAngle) {
112             return newAngle < previousAngle;
113         }
114     }
115 }