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 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 the circle
168 if (unwrappedEnd >= 0) {
169 addSubEdge(previousVertex, end,
170 length - alreadyManagedLength, outsideList);
171 } else {
172 // the edge is entirely outside 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 the circle
185 addSubEdge(previousVertex, end,
186 length - alreadyManagedLength, insideList);
187 } else {
188 // the edge is long enough to exit outside 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 }