1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.orekit.geometry.fov;
18
19 import java.util.ArrayList;
20 import java.util.List;
21
22 import org.hipparchus.geometry.enclosing.EnclosingBall;
23 import org.hipparchus.geometry.euclidean.threed.Line;
24 import org.hipparchus.geometry.euclidean.threed.Rotation;
25 import org.hipparchus.geometry.euclidean.threed.RotationConvention;
26 import org.hipparchus.geometry.euclidean.threed.Vector3D;
27 import org.hipparchus.geometry.partitioning.Region;
28 import org.hipparchus.geometry.spherical.twod.Edge;
29 import org.hipparchus.geometry.spherical.twod.S2Point;
30 import org.hipparchus.geometry.spherical.twod.Sphere2D;
31 import org.hipparchus.geometry.spherical.twod.SphericalPolygonsSet;
32 import org.hipparchus.geometry.spherical.twod.Vertex;
33 import org.hipparchus.util.FastMath;
34 import org.hipparchus.util.MathUtils;
35 import org.hipparchus.util.SinCos;
36 import org.orekit.bodies.GeodeticPoint;
37 import org.orekit.bodies.OneAxisEllipsoid;
38 import org.orekit.errors.OrekitException;
39 import org.orekit.errors.OrekitMessages;
40 import org.orekit.frames.Frame;
41 import org.orekit.frames.Transform;
42 import org.orekit.propagation.events.VisibilityTrigger;
43
44
45
46
47
48
49
50
51 public class PolygonalFieldOfView extends AbstractFieldOfView {
52
53
54 private final SphericalPolygonsSet zone;
55
56
57 private final EnclosingBall<Sphere2D, S2Point> cap;
58
59
60
61
62
63
64
65
66 public PolygonalFieldOfView(final SphericalPolygonsSet zone, final double margin) {
67 super(margin);
68 this.zone = zone;
69 this.cap = zone.getEnclosingCap();
70 }
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86 public PolygonalFieldOfView(final Vector3D center, final DefiningConeType coneType,
87 final Vector3D meridian, final double radius,
88 final int n, final double margin) {
89
90 super(margin);
91
92 final double verticesRadius = coneType.verticesRadius(radius, n);
93 final Vector3D vertex = coneType.createVertex(center, meridian, verticesRadius, n);
94 this.zone = new SphericalPolygonsSet(center, vertex, verticesRadius,
95 n, 1.0e-12 * verticesRadius);
96
97 final Rotation r = new Rotation(center, MathUtils.TWO_PI / n, RotationConvention.VECTOR_OPERATOR);
98 final S2Point[] support = new S2Point[n];
99 support[0] = new S2Point(vertex);
100 for (int i = 1; i < n; ++i) {
101 support[i] = new S2Point(r.applyTo(support[i - 1].getVector()));
102 }
103 this.cap = new EnclosingBall<>(new S2Point(center), Vector3D.angle(center, vertex), support);
104
105 }
106
107
108
109
110 public SphericalPolygonsSet getZone() {
111 return zone;
112 }
113
114
115 @Override
116 public double offsetFromBoundary(final Vector3D lineOfSight, final double angularRadius,
117 final VisibilityTrigger trigger) {
118
119 final S2Point los = new S2Point(lineOfSight);
120 final double margin = getMargin();
121 final double correctedRadius = trigger.radiusCorrection(angularRadius);
122 final double deadBand = margin + angularRadius;
123
124
125
126 final double crudeDistance = cap.getCenter().distance(los) - cap.getRadius();
127 if (crudeDistance > deadBand + 0.01) {
128
129
130 return crudeDistance + correctedRadius - margin;
131 }
132
133
134
135 return zone.projectToBoundary(los).getOffset() + correctedRadius - margin;
136
137 }
138
139
140 @Override
141 public Vector3D projectToBoundary(final Vector3D lineOfSight) {
142 return ((S2Point) zone.projectToBoundary(new S2Point(lineOfSight)).getProjected()).getVector();
143 }
144
145
146 @Override
147 public List<List<GeodeticPoint>> getFootprint(final Transform fovToBody,
148 final OneAxisEllipsoid body,
149 final double angularStep) {
150
151 final Frame bodyFrame = body.getBodyFrame();
152 final Vector3D position = fovToBody.transformPosition(Vector3D.ZERO);
153 final double r = position.getNorm();
154 if (body.isInside(position)) {
155 throw new OrekitException(OrekitMessages.POINT_INSIDE_ELLIPSOID);
156 }
157
158 final List<List<GeodeticPoint>> footprint = new ArrayList<>();
159
160 final List<Vertex> boundary = zone.getBoundaryLoops();
161 for (final Vertex loopStart : boundary) {
162 int count = 0;
163 final List<GeodeticPoint> loop = new ArrayList<>();
164 boolean intersectionsFound = false;
165 for (Edge edge = loopStart.getOutgoing();
166 count == 0 || edge.getStart() != loopStart;
167 edge = edge.getEnd().getOutgoing()) {
168 ++count;
169 final int n = (int) FastMath.ceil(edge.getLength() / angularStep);
170 final double delta = edge.getLength() / n;
171 for (int i = 0; i < n; ++i) {
172 final Vector3D awaySC = new Vector3D(r, edge.getPointAt(i * delta));
173 final Vector3D awayBody = fovToBody.transformPosition(awaySC);
174 final Line lineOfSight = new Line(position, awayBody, 1.0e-3);
175 GeodeticPoint gp = body.getIntersectionPoint(lineOfSight, position,
176 bodyFrame, null);
177 if (gp != null &&
178 Vector3D.dotProduct(awayBody.subtract(position),
179 body.transform(gp).subtract(position)) < 0) {
180
181
182 gp = null;
183 }
184
185 if (gp != null) {
186
187 intersectionsFound = true;
188 } else {
189
190
191 gp = body.transform(body.pointOnLimb(position, awayBody), bodyFrame, null);
192 }
193
194
195
196 loop.add(0, gp);
197
198 }
199 }
200
201 if (intersectionsFound) {
202
203
204 footprint.add(loop);
205 }
206
207 }
208
209 if (footprint.isEmpty()) {
210
211
212
213 final Vector3D bodyCenter = fovToBody.getStaticInverse().transformPosition(Vector3D.ZERO);
214 if (zone.checkPoint(new S2Point(bodyCenter)) != Region.Location.OUTSIDE) {
215
216
217 final Vector3D x = bodyCenter.orthogonal();
218 final Vector3D y = Vector3D.crossProduct(bodyCenter, x).normalize();
219 final double sinEta = body.getEquatorialRadius() / r;
220 final double sinEta2 = sinEta * sinEta;
221 final double cosAlpha = (FastMath.cos(angularStep) + sinEta2 - 1) / sinEta2;
222 final int n = (int) FastMath.ceil(MathUtils.TWO_PI / FastMath.acos(cosAlpha));
223 final double delta = MathUtils.TWO_PI / n;
224 final List<GeodeticPoint> loop = new ArrayList<>(n);
225 for (int i = 0; i < n; ++i) {
226 final SinCos sc = FastMath.sinCos(i * delta);
227 final Vector3D outside = new Vector3D(r * sc.cos(), x,
228 r * sc.sin(), y);
229 loop.add(body.transform(body.pointOnLimb(position, outside), bodyFrame, null));
230 }
231 footprint.add(loop);
232 }
233 }
234
235 return footprint;
236
237 }
238
239
240
241
242 public enum DefiningConeType {
243
244
245 INSIDE_CONE_TOUCHING_POLYGON_AT_EDGES_MIDDLE() {
246
247
248 @Override
249 protected double verticesRadius(final double radius, final int n) {
250
251 return FastMath.atan(FastMath.tan(radius) / FastMath.cos(FastMath.PI / n));
252 }
253
254
255 @Override
256 protected Vector3D createVertex(final Vector3D center, final Vector3D meridian,
257 final double verticesRadius, final int n) {
258
259 final SinCos scA = FastMath.sinCos(FastMath.PI / n);
260 final SinCos scR = FastMath.sinCos(verticesRadius);
261 final Vector3D z = center.normalize();
262 final Vector3D y = Vector3D.crossProduct(center, meridian).normalize();
263 final Vector3D x = Vector3D.crossProduct(y, z);
264 return new Vector3D(scR.sin() * scA.cos(), x, scR.sin() * scA.sin(), y, scR.cos(), z);
265 }
266
267 },
268
269
270 OUTSIDE_CONE_TOUCHING_POLYGON_AT_VERTICES() {
271
272
273 @Override
274 protected double verticesRadius(final double radius, final int n) {
275 return radius;
276 }
277
278
279 @Override
280 protected Vector3D createVertex(final Vector3D center, final Vector3D meridian,
281 final double verticesRadius, final int n) {
282
283 final SinCos scR = FastMath.sinCos(verticesRadius);
284 final Vector3D z = center.normalize();
285 final Vector3D y = Vector3D.crossProduct(center, meridian).normalize();
286 final Vector3D x = Vector3D.crossProduct(y, z);
287 return new Vector3D(scR.sin(), x, scR.cos(), z);
288 }
289
290 };
291
292
293
294
295
296
297 protected abstract double verticesRadius(double radius, int n);
298
299
300
301
302
303
304
305
306
307
308 protected abstract Vector3D createVertex(Vector3D center, Vector3D meridian,
309 double verticesRadius, int n);
310
311 }
312
313 }