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.models.earth.tessellation;
18  
19  import java.io.Serializable;
20  
21  import org.hipparchus.geometry.euclidean.threed.Vector3D;
22  import org.hipparchus.util.FastMath;
23  import org.orekit.bodies.GeodeticPoint;
24  
25  /** Simple data structure for a quadrilateral tile shape on a body surface.
26   * <p>
27   * This class is devoted to simple usage only. It assumes the edges
28   * are strictly between 0 and π radians and that the angles between
29   * edges are also strictly between 0 and π radians.
30   * </p>
31   * @see AlongTrackAiming
32   * @see ConstantAzimuthAiming
33   * @author Luc Maisonobe
34   */
35  public class Tile implements Serializable {
36  
37      /** Serializable UID. */
38      private static final long serialVersionUID = 20150313L;
39  
40      /** First vertex. */
41      private final GeodeticPoint v0;
42  
43      /** Second vertex. */
44      private final GeodeticPoint v1;
45  
46      /** Third vertex. */
47      private final GeodeticPoint v2;
48  
49      /** Fourth vertex. */
50      private final GeodeticPoint v3;
51  
52      /** Create a tile.
53       * <p>
54       * It is caller responsibility o ensure the vertices define a
55       * simple non-degenerated tile (i.e. edges are strictly between
56       * 0 than π radians and angles between edges are also strictly
57       * between 0 and π radians). No checks are performed here.
58       * </p>
59       * @param v0 first vertex
60       * @param v1 second vertex
61       * @param v2 third vertex
62       * @param v3 fourth vertex
63       */
64      public Tile(final GeodeticPoint v0, final GeodeticPoint v1,
65                  final GeodeticPoint v2, final GeodeticPoint v3) {
66          this.v0   = v0;
67          this.v1   = v1;
68          this.v2   = v2;
69          this.v3   = v3;
70      }
71  
72      /** Get the four vertices.
73       * @return four vertices
74       */
75      public GeodeticPoint[] getVertices() {
76          return new GeodeticPoint[] {
77              v0, v1, v2, v3
78          };
79      }
80  
81      /** Get an interpolated point inside the tile.
82       * <p>
83       * The interpolated point is based on bilinear interpolations
84       * along the body surface assumed to be <em>spherical</em>,
85       * and along the vertical axis.
86       * </p>
87       * <p>
88       * The interpolation parameters are chosen such that
89       * (u = 0, v = 0) maps to vertex v0, (u = 1, v = 0) maps
90       * to vertex v1, (u = 1, v = 1) maps to vertex v2 and
91       * (u = 0, v = 1) maps to vertex v3.
92       * </p>
93       * @param u first interpolation parameter (should be between
94       * 0 and 1 to remain inside the tile)
95       * @param v second interpolation parameter (should be between
96       * 0 and 1 to remain inside the tile)
97       * @return interpolated point
98       */
99      public GeodeticPoint getInterpolatedPoint(final double u, final double v) {
100 
101         // bilinear interpolation along a spherical shape
102         final Vector3D pu0 = interpolate(v0.getZenith(), v1.getZenith(), u);
103         final Vector3D pu1 = interpolate(v3.getZenith(), v2.getZenith(), u);
104         final Vector3D puv = interpolate(pu0, pu1, v);
105 
106         // bilinear interpolation of altitude
107         final double hu0 = v1.getAltitude() * u + v0.getAltitude() * (1 - u);
108         final double hu1 = v2.getAltitude() * u + v3.getAltitude() * (1 - u);
109         final double huv = hu1 * v + hu0 * (1 - v);
110 
111         // create interpolated point
112         return new GeodeticPoint(puv.getDelta(), puv.getAlpha(), huv);
113 
114     }
115 
116     /** Interpolate a vector along a unit sphere.
117      * @param p0 first base unit vector
118      * @param p1 second base unit vector
119      * @param r interpolation parameter (0 for p0, 1 for p1)
120      * @return interpolated unit vector
121      */
122     private Vector3D interpolate(final Vector3D p0, final Vector3D p1, final double r) {
123 
124         // find all interpolation angles
125         final double theta       = Vector3D.angle(p0, p1);
126         final double alpha       = r * theta;
127         final double thetaMAlpha = (1 - r) * theta;
128 
129         final double sinTheta       = FastMath.sin(theta);
130         final double sinAlpha       = FastMath.sin(alpha);
131         final double sinThetaMAlpha = FastMath.sin(thetaMAlpha);
132 
133         // interpolate
134         return new Vector3D(sinThetaMAlpha / sinTheta, p0, sinAlpha / sinTheta, p1);
135 
136     }
137 
138     /** Get the center point.
139      * <p>
140      * The center points corresponds to {@link
141      * #getInterpolatedPoint(double, double) getInterpolatedPoint(0.5, 0.5)}
142      * </p>
143      * @return center point
144      */
145     public GeodeticPoint getCenter() {
146         return getInterpolatedPoint(0.5, 0.5);
147     }
148 
149 }