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    *      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 }