1   /* Copyright 2002-2025 CS GROUP
2    * Licensed to CS GROUP (CS) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * CS 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    *   http://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  package org.orekit.geometry.fov;
18  
19  import org.hipparchus.exception.LocalizedCoreFormats;
20  import org.hipparchus.geometry.euclidean.threed.Rotation;
21  import org.hipparchus.geometry.euclidean.threed.RotationConvention;
22  import org.hipparchus.geometry.euclidean.threed.Vector3D;
23  import org.hipparchus.geometry.partitioning.Region;
24  import org.hipparchus.geometry.partitioning.RegionFactory;
25  import org.hipparchus.geometry.spherical.twod.Circle;
26  import org.hipparchus.geometry.spherical.twod.S2Point;
27  import org.hipparchus.geometry.spherical.twod.Sphere2D;
28  import org.hipparchus.geometry.spherical.twod.SphericalPolygonsSet;
29  import org.hipparchus.geometry.spherical.twod.SubCircle;
30  import org.hipparchus.util.FastMath;
31  import org.orekit.errors.OrekitException;
32  
33  /** Class representing a spacecraft sensor Field Of View with dihedral shape (i.e. rectangular shape).
34   * @author Luc Maisonobe
35   * @since 10.1
36   */
37  public class DoubleDihedraFieldOfView extends PolygonalFieldOfView {
38  
39      /** Build a Field Of View with dihedral shape (i.e. rectangular shape).
40       * @param center Direction of the FOV center, in spacecraft frame
41       * @param axis1 FOV dihedral axis 1, in spacecraft frame
42       * @param halfAperture1 FOV dihedral half aperture angle 1,
43       * must be less than π/2, i.e. full dihedra must be smaller then
44       * an hemisphere
45       * @param axis2 FOV dihedral axis 2, in spacecraft frame
46       * @param halfAperture2 FOV dihedral half aperture angle 2,
47       * must be less than π/2, i.e. full dihedra must be smaller then
48       * an hemisphere
49       * @param margin angular margin to apply to the zone (if positive,
50       * points outside of the raw FoV but close enough to the boundary are
51       * considered visible; if negative, points inside of the raw FoV
52       * but close enough to the boundary are considered not visible)
53       */
54      public DoubleDihedraFieldOfView(final Vector3D center,
55                                      final Vector3D axis1, final double halfAperture1,
56                                      final Vector3D axis2, final double halfAperture2,
57                                      final double margin) {
58          super(createPolygon(center, axis1, halfAperture1, axis2, halfAperture2), margin);
59      }
60  
61      /** Create polygon.
62       * @param center Direction of the FOV center, in spacecraft frame
63       * @param axis1 FOV dihedral axis 1, in spacecraft frame
64       * @param halfAperture1 FOV dihedral half aperture angle 1,
65       * must be less than π/2, i.e. full dihedra must be smaller then
66       * an hemisphere
67       * @param axis2 FOV dihedral axis 2, in spacecraft frame
68       * @param halfAperture2 FOV dihedral half aperture angle 2,
69       * must be less than π/2, i.e. full dihedra must be smaller then
70       * an hemisphere
71       * @return built polygon
72       */
73      private static SphericalPolygonsSet createPolygon(final Vector3D center,
74                                                        final Vector3D axis1, final double halfAperture1,
75                                                        final Vector3D axis2, final double halfAperture2) {
76          final RegionFactory<Sphere2D, S2Point, Circle, SubCircle> factory = new RegionFactory<>();
77          final double tolerance = FastMath.max(FastMath.ulp(2.0 * FastMath.PI),
78                                                1.0e-12 * FastMath.max(halfAperture1, halfAperture2));
79          final Region<Sphere2D, S2Point, Circle, SubCircle> dihedra1 = buildDihedra(factory, tolerance, center, axis1, halfAperture1);
80          final Region<Sphere2D, S2Point, Circle, SubCircle> dihedra2 = buildDihedra(factory, tolerance, center, axis2, halfAperture2);
81          return (SphericalPolygonsSet) factory.intersection(dihedra1, dihedra2);
82      }
83  
84      /** Build a dihedra.
85       * @param factory factory for regions
86       * @param tolerance tolerance below which points are considered equal
87       * @param center Direction of the FOV center, in spacecraft frame
88       * @param axis FOV dihedral axis, in spacecraft frame
89       * @param halfAperture FOV dihedral half aperture angle,
90       * must be less than π/2, i.e. full dihedra must be smaller then
91       * an hemisphere
92       * @return dihedra
93       */
94      private static Region<Sphere2D, S2Point, Circle, SubCircle>
95          buildDihedra(final RegionFactory<Sphere2D, S2Point, Circle, SubCircle> factory,
96                       final double tolerance, final Vector3D center,
97                       final Vector3D axis, final double halfAperture) {
98          if (halfAperture > 0.5 * FastMath.PI) {
99              throw new OrekitException(LocalizedCoreFormats.OUT_OF_RANGE_SIMPLE,
100                                       halfAperture, 0.0, 0.5 * FastMath.PI);
101         }
102 
103         final Rotation r = new Rotation(axis, halfAperture, RotationConvention.VECTOR_OPERATOR);
104         final Vector3D normalCenterPlane = Vector3D.crossProduct(axis, center);
105         final Vector3D normalSidePlus    = r.applyInverseTo(normalCenterPlane);
106         final Vector3D normalSideMinus   = r.applyTo(normalCenterPlane.negate());
107 
108         return factory.intersection(new SphericalPolygonsSet(normalSidePlus,  tolerance),
109                                     new SphericalPolygonsSet(normalSideMinus, tolerance));
110 
111     }
112 
113 }