| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.commons.geometry.spherical.twod; |
| |
| import java.util.Collections; |
| import java.util.List; |
| |
| import org.apache.commons.geometry.core.Transform; |
| import org.apache.commons.geometry.core.partitioning.ConvexSubHyperplane; |
| import org.apache.commons.geometry.core.partitioning.Hyperplane; |
| import org.apache.commons.geometry.core.partitioning.Split; |
| import org.apache.commons.geometry.core.partitioning.SplitLocation; |
| import org.apache.commons.geometry.core.precision.DoublePrecisionContext; |
| import org.apache.commons.geometry.spherical.oned.AngularInterval; |
| import org.apache.commons.geometry.spherical.oned.CutAngle; |
| import org.apache.commons.geometry.spherical.oned.Point1S; |
| import org.apache.commons.geometry.spherical.oned.Transform1S; |
| |
| /** Class representing a single, <em>convex</em> angular interval in a {@link GreatCircle}. Convex |
| * angular intervals are those where the shortest path between all pairs of points in the |
| * interval are completely contained in the interval. In the case of paths that tie for the |
| * shortest length, it is sufficient that one of the paths is completely contained in the |
| * interval. In spherical 2D space, convex arcs either fill the entire great circle or have |
| * an angular size of less than or equal to {@code pi} radians. |
| * |
| * <p>Instances of this class are guaranteed to be immutable.</p> |
| */ |
| public final class GreatArc extends AbstractSubGreatCircle implements ConvexSubHyperplane<Point2S> { |
| /** The interval representing the region of the great circle contained in the arc. |
| */ |
| private final AngularInterval.Convex interval; |
| |
| /** Create a new instance from a great circle and the interval embedded in it. |
| * @param circle defining great circle instance |
| * @param interval convex angular interval embedded in the great circle |
| */ |
| private GreatArc(final GreatCircle circle, final AngularInterval.Convex interval) { |
| super(circle); |
| |
| this.interval = interval; |
| } |
| |
| /** Return the start point of the arc, or null if the arc represents the full space. |
| * @return the start point of the arc, or null if the arc represents the full space. |
| */ |
| public Point2S getStartPoint() { |
| if (!interval.isFull()) { |
| return getCircle().toSpace(interval.getMinBoundary().getPoint()); |
| } |
| |
| return null; |
| } |
| |
| /** Return the end point of the arc, or null if the arc represents the full space. |
| * @return the end point of the arc, or null if the arc represents the full space. |
| */ |
| public Point2S getEndPoint() { |
| if (!interval.isFull()) { |
| return getCircle().toSpace(interval.getMaxBoundary().getPoint()); |
| } |
| |
| return null; |
| } |
| |
| /** Return the midpoint of the arc, or null if the arc represents the full space. |
| * @return the midpoint of the arc, or null if the arc represents the full space. |
| */ |
| public Point2S getMidPoint() { |
| if (!interval.isFull()) { |
| return getCircle().toSpace(interval.getMidPoint()); |
| } |
| |
| return null; |
| } |
| |
| /** Get the angular interval for the arc. |
| * @return the angular interval for the arc |
| * @see #getSubspaceRegion() |
| */ |
| public AngularInterval.Convex getInterval() { |
| return interval; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public AngularInterval.Convex getSubspaceRegion() { |
| return getInterval(); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public List<GreatArc> toConvex() { |
| return Collections.singletonList(this); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public Split<GreatArc> split(final Hyperplane<Point2S> splitter) { |
| final GreatCircle splitterCircle = (GreatCircle) splitter; |
| final GreatCircle thisCircle = getCircle(); |
| |
| final Point2S intersection = splitterCircle.intersection(thisCircle); |
| |
| GreatArc minus = null; |
| GreatArc plus = null; |
| |
| if (intersection != null) { |
| // use a negative-facing cut angle to account for the fact that the great circle |
| // poles point to the minus side of the circle |
| final CutAngle subSplitter = CutAngle.createNegativeFacing( |
| thisCircle.toSubspace(intersection), splitterCircle.getPrecision()); |
| |
| final Split<AngularInterval.Convex> subSplit = interval.splitDiameter(subSplitter); |
| final SplitLocation subLoc = subSplit.getLocation(); |
| |
| if (subLoc == SplitLocation.MINUS) { |
| minus = this; |
| } else if (subLoc == SplitLocation.PLUS) { |
| plus = this; |
| } else if (subLoc == SplitLocation.BOTH) { |
| minus = GreatArc.fromInterval(thisCircle, subSplit.getMinus()); |
| plus = GreatArc.fromInterval(thisCircle, subSplit.getPlus()); |
| } |
| } |
| |
| return new Split<>(minus, plus); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public GreatArc transform(final Transform<Point2S> transform) { |
| return new GreatArc(getCircle().transform(transform), interval); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public GreatArc reverse() { |
| return new GreatArc( |
| getCircle().reverse(), |
| interval.transform(Transform1S.createNegation())); |
| } |
| |
| /** Return a string representation of this great arc. |
| * |
| * <p>In order to keep the string representation short but useful, the exact format of the return |
| * value depends on the properties of the arc. See below for examples. |
| * |
| * <ul> |
| * <li>Full arc |
| * <ul> |
| * <li>{@code GreatArc[full= true, circle= GreatCircle[pole= (0.0, 0.0, 1.0), x= (1.0, 0.0, 0.0), y= (0.0, 1.0, 0.0)]}</li> |
| * </ul> |
| * </li> |
| * <li>Non-full arc |
| * <ul> |
| * <li>{@code GreatArc[start= (1.0, 1.5707963267948966), end= (2.0, 1.5707963267948966)}</li> |
| * </ul> |
| * </li> |
| * </ul> |
| */ |
| @Override |
| public String toString() { |
| final StringBuilder sb = new StringBuilder(); |
| sb.append(this.getClass().getSimpleName()) |
| .append("["); |
| |
| if (isFull()) { |
| sb.append("full= true, circle= ") |
| .append(getCircle()); |
| } else { |
| sb.append("start= ") |
| .append(getStartPoint()) |
| .append(", end= ") |
| .append(getEndPoint()); |
| } |
| |
| return sb.toString(); |
| } |
| |
| /** Construct an arc along the shortest path between the given points. The underlying |
| * great circle is oriented in the direction from {@code start} to {@code end}. |
| * @param start start point for the interval |
| * @param end end point point for the interval |
| * @param precision precision context used to compare floating point numbers |
| * @return an arc representing the shortest path between the given points |
| * @throws org.apache.commons.geometry.core.exception.GeometryException if either of the given points is |
| * NaN or infinite, or if the given points are equal or antipodal as evaluated by the given precision context |
| * @see GreatCircle#fromPoints(Point2S, Point2S, org.apache.commons.geometry.core.precision.DoublePrecisionContext) |
| */ |
| public static GreatArc fromPoints(final Point2S start, final Point2S end, final DoublePrecisionContext precision) { |
| final GreatCircle circle = GreatCircle.fromPoints(start, end, precision); |
| |
| final Point1S subspaceStart = circle.toSubspace(start); |
| final Point1S subspaceEnd = circle.toSubspace(end); |
| final AngularInterval.Convex interval = AngularInterval.Convex.of(subspaceStart, subspaceEnd, precision); |
| |
| return fromInterval(circle, interval); |
| } |
| |
| /** Construct an arc from a great circle and an angular interval. |
| * @param circle circle defining the arc |
| * @param interval interval representing the portion of the circle contained |
| * in the arc |
| * @return an arc created from the given great circle and interval |
| */ |
| public static GreatArc fromInterval(final GreatCircle circle, final AngularInterval.Convex interval) { |
| return new GreatArc(circle, interval); |
| } |
| } |