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 * https://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 18 /* 19 * This is not the original file distributed by the Apache Software Foundation 20 * It has been modified by the Hipparchus project 21 */ 22 package org.hipparchus.geometry.spherical.twod; 23 24 import java.util.List; 25 26 import org.hipparchus.geometry.euclidean.threed.Vector3D; 27 import org.hipparchus.geometry.spherical.oned.Arc; 28 import org.hipparchus.util.FastMath; 29 import org.hipparchus.util.MathUtils; 30 31 /** Spherical polygons boundary edge. 32 * @see SphericalPolygonsSet#getBoundaryLoops() 33 * @see Vertex 34 */ 35 public class Edge { 36 37 /** Start vertex. */ 38 private final Vertex start; 39 40 /** End vertex. */ 41 private Vertex end; 42 43 /** Length of the arc. */ 44 private double length; 45 46 /** Circle supporting the edge. */ 47 private final Circle circle; 48 49 /** Build an edge not contained in any node yet. 50 * @param start start vertex 51 * @param end end vertex 52 * @param length length of the arc (it can be greater than \( \pi \)) 53 * @param circle circle supporting the edge 54 */ 55 Edge(final Vertex start, final Vertex end, final double length, final Circle circle) { 56 57 this.start = start; 58 this.end = end; 59 this.length = length; 60 this.circle = circle; 61 62 // connect the vertices back to the edge 63 start.setOutgoing(this); 64 end.setIncoming(this); 65 66 } 67 68 /** Get start vertex. 69 * @return start vertex 70 */ 71 public Vertex getStart() { 72 return start; 73 } 74 75 /** Get end vertex. 76 * @return end vertex 77 */ 78 public Vertex getEnd() { 79 return end; 80 } 81 82 /** Get the length of the arc. 83 * @return length of the arc (can be greater than \( \pi \)) 84 */ 85 public double getLength() { 86 return length; 87 } 88 89 /** Get the circle supporting this edge. 90 * @return circle supporting this edge 91 */ 92 public Circle getCircle() { 93 return circle; 94 } 95 96 /** Get an intermediate point. 97 * <p> 98 * The angle along the edge should normally be between 0 and {@link #getLength()} 99 * in order to remain within edge limits. However, there are no checks on the 100 * value of the angle, so user can rebuild the full circle on which an edge is 101 * defined if they want. 102 * </p> 103 * @param alpha angle along the edge, counted from {@link #getStart()} 104 * @return an intermediate point 105 */ 106 public Vector3D getPointAt(final double alpha) { 107 return circle.getPointAt(alpha + circle.getPhase(start.getLocation().getVector())); 108 } 109 110 /** Set the length. 111 * @param length new length 112 */ 113 void setLength(final double length) { 114 this.length = length; 115 } 116 117 /** Connect the instance with a following edge. 118 * @param next edge following the instance 119 */ 120 void setNextEdge(final Edge next) { 121 end = next.getStart(); 122 end.setIncoming(this); 123 } 124 125 /** Split the edge. 126 * <p> 127 * Once split, this edge is not referenced anymore by the vertices, 128 * it is replaced by the two or three sub-edges and intermediate splitting 129 * vertices are introduced to connect these sub-edges together. 130 * </p> 131 * @param splitCircle circle splitting the edge in several parts 132 * @param outsideList list where to put parts that are outside of the split circle 133 * @param insideList list where to put parts that are inside the split circle 134 */ 135 void split(final Circle splitCircle, final List<Edge> outsideList, final List<Edge> insideList) { 136 137 // get the inside arc, synchronizing its phase with the edge itself 138 final double edgeStart = circle.getPhase(start.getLocation().getVector()); 139 final Arc arc = circle.getInsideArc(splitCircle); 140 final double arcRelativeStart = MathUtils.normalizeAngle(arc.getInf(), edgeStart + FastMath.PI) - edgeStart; 141 final double arcRelativeEnd = arcRelativeStart + arc.getSize(); 142 final double unwrappedEnd = arcRelativeEnd - MathUtils.TWO_PI; 143 144 // build the sub-edges 145 final double tolerance = circle.getTolerance(); 146 Vertex previousVertex = start; 147 if (unwrappedEnd >= length - tolerance) { 148 149 // the edge is entirely contained inside the circle 150 // we don't split anything 151 insideList.add(this); 152 153 } else { 154 155 // there are at least some parts of the edge that should be outside 156 // (even is they are later be filtered out as being too small) 157 double alreadyManagedLength = 0; 158 if (unwrappedEnd >= 0) { 159 // the start of the edge is inside the circle 160 previousVertex = addSubEdge(previousVertex, 161 new Vertex(new S2Point(circle.getPointAt(edgeStart + unwrappedEnd))), 162 unwrappedEnd, insideList); 163 alreadyManagedLength = unwrappedEnd; 164 } 165 166 if (arcRelativeStart >= length - tolerance) { 167 // the edge ends while still outside of the circle 168 if (unwrappedEnd >= 0) { 169 addSubEdge(previousVertex, end, 170 length - alreadyManagedLength, outsideList); 171 } else { 172 // the edge is entirely outside of the circle 173 // we don't split anything 174 outsideList.add(this); 175 } 176 } else { 177 // the edge is long enough to enter inside the circle 178 previousVertex = addSubEdge(previousVertex, 179 new Vertex(new S2Point(circle.getPointAt(edgeStart + arcRelativeStart))), 180 arcRelativeStart - alreadyManagedLength, outsideList); 181 alreadyManagedLength = arcRelativeStart; 182 183 if (arcRelativeEnd >= length - tolerance) { 184 // the edge ends while still inside of the circle 185 addSubEdge(previousVertex, end, 186 length - alreadyManagedLength, insideList); 187 } else { 188 // the edge is long enough to exit outside of the circle 189 previousVertex = addSubEdge(previousVertex, 190 new Vertex(new S2Point(circle.getPointAt(edgeStart + arcRelativeEnd))), 191 arcRelativeEnd - alreadyManagedLength, insideList); 192 alreadyManagedLength = arcRelativeEnd; 193 addSubEdge(previousVertex, end, 194 length - alreadyManagedLength, outsideList); 195 } 196 } 197 198 } 199 200 } 201 202 /** Add a sub-edge to a list if long enough. 203 * <p> 204 * If the length of the sub-edge to add is smaller than the {@link Circle#getTolerance()} 205 * tolerance of the support circle, it will be ignored. 206 * </p> 207 * @param subStart start of the sub-edge 208 * @param subEnd end of the sub-edge 209 * @param subLength length of the sub-edge 210 * @param list list where to put the sub-edge 211 * @return end vertex of the edge ({@code subEnd} if the edge was long enough and really 212 * added, {@code subStart} if the edge was too small and therefore ignored) 213 */ 214 private Vertex addSubEdge(final Vertex subStart, final Vertex subEnd, final double subLength, final List<Edge> list) { 215 216 if (subLength <= circle.getTolerance()) { 217 // the edge is too short, we ignore it 218 return subStart; 219 } 220 221 // really add the edge 222 final Edge edge = new Edge(subStart, subEnd, subLength, circle); 223 list.add(edge); 224 return subEnd; 225 226 } 227 228 }