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.Collections; 020import java.util.List; 021 022import org.apache.commons.geometry.core.Transform; 023import org.apache.commons.geometry.core.partitioning.Hyperplane; 024import org.apache.commons.geometry.core.partitioning.HyperplaneConvexSubset; 025import org.apache.commons.geometry.core.partitioning.Split; 026import org.apache.commons.geometry.core.partitioning.SplitLocation; 027import org.apache.commons.geometry.spherical.oned.AngularInterval; 028import org.apache.commons.geometry.spherical.oned.CutAngle; 029import org.apache.commons.geometry.spherical.oned.CutAngles; 030import org.apache.commons.geometry.spherical.oned.Transform1S; 031 032/** Class representing a single, <em>convex</em> angular interval in a {@link GreatCircle}. Convex 033 * angular intervals are those where the shortest path between all pairs of points in the 034 * interval are completely contained in the interval. In the case of paths that tie for the 035 * shortest length, it is sufficient that one of the paths is completely contained in the 036 * interval. In spherical 2D space, convex arcs either fill the entire great circle or have 037 * an angular size of less than or equal to {@code pi} radians. 038 * 039 * <p>Instances of this class are guaranteed to be immutable.</p> 040 * @see GreatCircles 041 */ 042public final class GreatArc extends GreatCircleSubset implements HyperplaneConvexSubset<Point2S> { 043 /** The interval representing the region of the great circle contained in the arc. 044 */ 045 private final AngularInterval.Convex interval; 046 047 /** Create a new instance from a great circle and the interval embedded in it. 048 * @param circle defining great circle instance 049 * @param interval convex angular interval embedded in the great circle 050 */ 051 GreatArc(final GreatCircle circle, final AngularInterval.Convex interval) { 052 super(circle); 053 054 this.interval = interval; 055 } 056 057 /** Return the start point of the arc, or null if the arc represents the full space. 058 * @return the start point of the arc, or null if the arc represents the full space. 059 */ 060 public Point2S getStartPoint() { 061 if (!interval.isFull()) { 062 return getCircle().toSpace(interval.getMinBoundary().getPoint()); 063 } 064 065 return null; 066 } 067 068 /** Return the end point of the arc, or null if the arc represents the full space. 069 * @return the end point of the arc, or null if the arc represents the full space. 070 */ 071 public Point2S getEndPoint() { 072 if (!interval.isFull()) { 073 return getCircle().toSpace(interval.getMaxBoundary().getPoint()); 074 } 075 076 return null; 077 } 078 079 /** Return the midpoint of the arc, or null if the arc represents the full space. 080 * @return the midpoint of the arc, or null if the arc represents the full space. 081 */ 082 public Point2S getMidPoint() { 083 if (!interval.isFull()) { 084 return getCircle().toSpace(interval.getMidPoint()); 085 } 086 087 return null; 088 } 089 090 /** Get the angular interval for the arc. 091 * @return the angular interval for the arc 092 * @see #getSubspaceRegion() 093 */ 094 public AngularInterval.Convex getInterval() { 095 return interval; 096 } 097 098 /** {@inheritDoc} */ 099 @Override 100 public AngularInterval.Convex getSubspaceRegion() { 101 return getInterval(); 102 } 103 104 /** {@inheritDoc} */ 105 @Override 106 public List<GreatArc> toConvex() { 107 return Collections.singletonList(this); 108 } 109 110 /** {@inheritDoc} */ 111 @Override 112 public Split<GreatArc> split(final Hyperplane<Point2S> splitter) { 113 final GreatCircle splitterCircle = (GreatCircle) splitter; 114 final GreatCircle thisCircle = getCircle(); 115 116 final Point2S intersection = splitterCircle.intersection(thisCircle); 117 118 GreatArc minus = null; 119 GreatArc plus = null; 120 121 if (intersection != null) { 122 // use a negative-facing cut angle to account for the fact that the great circle 123 // poles point to the minus side of the circle 124 final CutAngle subSplitter = CutAngles.createNegativeFacing( 125 thisCircle.toSubspace(intersection), splitterCircle.getPrecision()); 126 127 final Split<AngularInterval.Convex> subSplit = interval.splitDiameter(subSplitter); 128 final SplitLocation subLoc = subSplit.getLocation(); 129 130 if (subLoc == SplitLocation.MINUS) { 131 minus = this; 132 } else if (subLoc == SplitLocation.PLUS) { 133 plus = this; 134 } else if (subLoc == SplitLocation.BOTH) { 135 minus = GreatCircles.arcFromInterval(thisCircle, subSplit.getMinus()); 136 plus = GreatCircles.arcFromInterval(thisCircle, subSplit.getPlus()); 137 } 138 } 139 140 return new Split<>(minus, plus); 141 } 142 143 /** {@inheritDoc} */ 144 @Override 145 public GreatArc transform(final Transform<Point2S> transform) { 146 return new GreatArc(getCircle().transform(transform), interval); 147 } 148 149 /** {@inheritDoc} */ 150 @Override 151 public GreatArc reverse() { 152 return new GreatArc( 153 getCircle().reverse(), 154 interval.transform(Transform1S.createNegation())); 155 } 156 157 /** Return a string representation of this great arc. 158 * 159 * <p>In order to keep the string representation short but useful, the exact format of the return 160 * value depends on the properties of the arc. See below for examples. 161 * 162 * <ul> 163 * <li>Full arc 164 * <ul> 165 * <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> 166 * </ul> 167 * </li> 168 * <li>Non-full arc 169 * <ul> 170 * <li>{@code GreatArc[start= (1.0, 1.5707963267948966), end= (2.0, 1.5707963267948966)}</li> 171 * </ul> 172 * </li> 173 * </ul> 174 */ 175 @Override 176 public String toString() { 177 final StringBuilder sb = new StringBuilder(); 178 sb.append(this.getClass().getSimpleName()).append('['); 179 180 if (isFull()) { 181 sb.append("full= true, circle= ") 182 .append(getCircle()); 183 } else { 184 sb.append("start= ") 185 .append(getStartPoint()) 186 .append(", end= ") 187 .append(getEndPoint()); 188 } 189 190 return sb.toString(); 191 } 192}