001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.geometry.spherical.twod;
018
019import java.util.Collection;
020import java.util.List;
021
022/** Great arc connector that selects between multiple connection options
023 * based on the resulting interior angle. An interior angle in this
024 * case is the angle created between an incoming arc and an outgoing arc
025 * as measured on the minus (interior) side of the incoming arc. If looking
026 * along the direction of the incoming arc, smaller interior angles
027 * point more to the left and larger ones point more to the right.
028 *
029 * <p>This class provides two concrete implementations: {@link Maximize} and
030 * {@link Minimize}, which choose connections with the largest or smallest interior
031 * angles respectively.
032 * </p>
033 */
034public abstract class InteriorAngleGreatArcConnector extends AbstractGreatArcConnector {
035    /** {@inheritDoc} */
036    @Override
037    protected ConnectableGreatArc selectConnection(final ConnectableGreatArc incoming,
038            final List<ConnectableGreatArc> outgoing) {
039
040        // search for the best connection
041        final GreatCircle circle = incoming.getArc().getCircle();
042
043        double selectedInteriorAngle = Double.POSITIVE_INFINITY;
044        ConnectableGreatArc selected = null;
045
046        for (final ConnectableGreatArc candidate : outgoing) {
047            final double interiorAngle = Math.PI - circle.angle(candidate.getArc().getCircle(),
048                    incoming.getArc().getEndPoint());
049
050            if (selected == null || isBetterAngle(interiorAngle, selectedInteriorAngle)) {
051                selectedInteriorAngle = interiorAngle;
052                selected = candidate;
053            }
054        }
055
056        return selected;
057    }
058
059    /** Return true if {@code newAngle} represents a better interior angle than {@code previousAngle}.
060     * @param newAngle the new angle under consideration
061     * @param previousAngle the previous best angle
062     * @return true if {@code newAngle} represents a better interior angle than {@code previousAngle}
063     */
064    protected abstract boolean isBetterAngle(double newAngle, double previousAngle);
065
066    /** Convenience method for connecting a set of arcs with interior angles maximized
067     * when possible. This method is equivalent to {@code new Maximize().connect(segments)}.
068     * @param arcs arcs to connect
069     * @return a list of connected arc paths
070     * @see Maximize
071     */
072    public static List<GreatArcPath> connectMaximized(final Collection<GreatArc> arcs) {
073        return new Maximize().connectAll(arcs);
074    }
075
076    /** Convenience method for connecting a set of line segments with interior angles minimized
077     * when possible. This method is equivalent to {@code new Minimize().connect(segments)}.
078     * @param arcs arcs to connect
079     * @return a list of connected arc paths
080     * @see Minimize
081     */
082    public static List<GreatArcPath> connectMinimized(final Collection<GreatArc> arcs) {
083        return new Minimize().connectAll(arcs);
084    }
085
086    /** Implementation of {@link InteriorAngleGreatArcConnector} that chooses arc
087     * connections that produce the largest interior angles. Another way to visualize this is
088     * that when presented multiple connection options for a given arc, this class will
089     * choose the option that points most to the right when viewed in the direction of the incoming
090     * arc.
091     */
092    public static class Maximize extends InteriorAngleGreatArcConnector {
093        /** {@inheritDoc} */
094        @Override
095        protected boolean isBetterAngle(final double newAngle, final double previousAngle) {
096            return newAngle > previousAngle;
097        }
098    }
099
100    /** Implementation of {@link InteriorAngleGreatArcConnector} that chooses arc
101     * connections that produce the smallest interior angles. Another way to visualize this is
102     * that when presented multiple connection options for a given arc, this class will
103     * choose the option that points most to the left when viewed in the direction of the incoming
104     * arc.
105     */
106    public static class Minimize extends InteriorAngleGreatArcConnector {
107        /** {@inheritDoc} */
108        @Override
109        protected boolean isBetterAngle(final double newAngle, final double previousAngle) {
110            return newAngle < previousAngle;
111        }
112    }
113}