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