Ellipse.java

  1. /* Copyright 2002-2024 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.bodies;

  18. import java.io.Serializable;

  19. import org.hipparchus.geometry.euclidean.threed.Vector3D;
  20. import org.hipparchus.geometry.euclidean.twod.Vector2D;
  21. import org.hipparchus.util.FastMath;
  22. import org.hipparchus.util.MathArrays;
  23. import org.hipparchus.util.SinCos;
  24. import org.orekit.frames.Frame;
  25. import org.orekit.utils.TimeStampedPVCoordinates;

  26. /**
  27.  * Model of a 2D ellipse in 3D space.
  28.  * <p>
  29.  * These ellipses are mainly created as plane sections of general 3D ellipsoids,
  30.  * but can be used for other purposes.
  31.  * </p>
  32.  * <p>
  33.  * Instances of this class are guaranteed to be immutable.
  34.  * </p>
  35.  * @see Ellipsoid#getPlaneSection(Vector3D, Vector3D)
  36.  * @since 7.0
  37.  * @author Luc Maisonobe
  38.  */
  39. public class Ellipse implements Serializable {

  40.     /** Serializable UID. */
  41.     private static final long serialVersionUID = 20140925L;

  42.     /** Convergence limit. */
  43.     private static final double ANGULAR_THRESHOLD = 1.0e-12;

  44.     /** Center of the 2D ellipse. */
  45.     private final Vector3D center;

  46.     /** Unit vector along the major axis. */
  47.     private final Vector3D u;

  48.     /** Unit vector along the minor axis. */
  49.     private final Vector3D v;

  50.     /** Semi major axis. */
  51.     private final double a;

  52.     /** Semi minor axis. */
  53.     private final double b;

  54.     /** Frame in which the ellipse is defined. */
  55.     private final Frame frame;

  56.     /** Semi major axis radius power 2. */
  57.     private final double a2;

  58.     /** Semi minor axis power 2. */
  59.     private final double b2;

  60.     /** Eccentricity power 2. */
  61.     private final double e2;

  62.     /** 1 minus flatness. */
  63.     private final double g;

  64.     /** g * g. */
  65.     private final double g2;

  66.     /** Evolute factor along major axis. */
  67.     private final double evoluteFactorX;

  68.     /** Evolute factor along minor axis. */
  69.     private final double evoluteFactorY;

  70.     /** Simple constructor.
  71.      * @param center center of the 2D ellipse
  72.      * @param u unit vector along the major axis
  73.      * @param v unit vector along the minor axis
  74.      * @param a semi major axis
  75.      * @param b semi minor axis
  76.      * @param frame frame in which the ellipse is defined
  77.      */
  78.     public Ellipse(final Vector3D center, final Vector3D u,
  79.                    final Vector3D v, final double a, final double b,
  80.                    final Frame frame) {
  81.         this.center = center;
  82.         this.u      = u;
  83.         this.v      = v;
  84.         this.a      = a;
  85.         this.b      = b;
  86.         this.frame  = frame;
  87.         this.a2     = a * a;
  88.         this.g      = b / a;
  89.         this.g2     = g * g;
  90.         this.e2     = 1 - g2;
  91.         this.b2     = b * b;
  92.         this.evoluteFactorX = (a2 - b2) / (a2 * a2);
  93.         this.evoluteFactorY = (b2 - a2) / (b2 * b2);
  94.     }

  95.     /** Get the center of the 2D ellipse.
  96.      * @return center of the 2D ellipse
  97.      */
  98.     public Vector3D getCenter() {
  99.         return center;
  100.     }

  101.     /** Get the unit vector along the major axis.
  102.      * @return unit vector along the major axis
  103.      */
  104.     public Vector3D getU() {
  105.         return u;
  106.     }

  107.     /** Get the unit vector along the minor axis.
  108.      * @return unit vector along the minor axis
  109.      */
  110.     public Vector3D getV() {
  111.         return v;
  112.     }

  113.     /** Get the semi major axis.
  114.      * @return semi major axis
  115.      */
  116.     public double getA() {
  117.         return a;
  118.     }

  119.     /** Get the semi minor axis.
  120.      * @return semi minor axis
  121.      */
  122.     public double getB() {
  123.         return b;
  124.     }

  125.     /** Get the defining frame.
  126.      * @return defining frame
  127.      */
  128.     public Frame getFrame() {
  129.         return frame;
  130.     }

  131.     /** Get a point of the 2D ellipse.
  132.      * @param theta angular parameter on the ellipse (really the eccentric anomaly)
  133.      * @return ellipse point at theta, in underlying ellipsoid frame
  134.      */
  135.     public Vector3D pointAt(final double theta) {
  136.         final SinCos scTheta = FastMath.sinCos(theta);
  137.         return toSpace(new Vector2D(a * scTheta.cos(), b * scTheta.sin()));
  138.     }

  139.     /** Create a point from its ellipse-relative coordinates.
  140.      * @param p point defined with respect to ellipse
  141.      * @return point defined with respect to 3D frame
  142.      * @see #toPlane(Vector3D)
  143.      */
  144.     public Vector3D toSpace(final Vector2D p) {
  145.         return new Vector3D(1, center, p.getX(), u, p.getY(), v);
  146.     }

  147.     /** Project a point to the ellipse plane.
  148.      * @param p point defined with respect to 3D frame
  149.      * @return point defined with respect to ellipse
  150.      * @see #toSpace(Vector2D)
  151.      */
  152.     public Vector2D toPlane(final Vector3D p) {
  153.         final Vector3D delta = p.subtract(center);
  154.         return new Vector2D(Vector3D.dotProduct(delta, u), Vector3D.dotProduct(delta, v));
  155.     }

  156.     /** Find the closest ellipse point.
  157.      * @param p point in the ellipse plane to project on the ellipse itself
  158.      * @return closest point belonging to 2D meridian ellipse
  159.      */
  160.     public Vector2D projectToEllipse(final Vector2D p) {

  161.         final double x = FastMath.abs(p.getX());
  162.         final double y = p.getY();

  163.         if (x <= ANGULAR_THRESHOLD * FastMath.abs(y)) {
  164.             // the point is almost on the minor axis, approximate the ellipse with
  165.             // the osculating circle whose center is at evolute cusp along minor axis
  166.             final double osculatingRadius = a2 / b;
  167.             final double evoluteCuspZ     = FastMath.copySign(a * e2 / g, -y);
  168.             final double deltaZ           = y - evoluteCuspZ;
  169.             final double ratio            = osculatingRadius / FastMath.hypot(deltaZ, x);
  170.             return new Vector2D(FastMath.copySign(ratio * x, p.getX()),
  171.                                 evoluteCuspZ + ratio * deltaZ);
  172.         }

  173.         if (FastMath.abs(y) <= ANGULAR_THRESHOLD * x) {
  174.             // the point is almost on the major axis

  175.             final double osculatingRadius = b2 / a;
  176.             final double evoluteCuspR     = a * e2;
  177.             final double deltaR           = x - evoluteCuspR;
  178.             if (deltaR >= 0) {
  179.                 // the point is outside of the ellipse evolute, approximate the ellipse
  180.                 // with the osculating circle whose center is at evolute cusp along major axis
  181.                 final double ratio = osculatingRadius / FastMath.hypot(y, deltaR);
  182.                 return new Vector2D(FastMath.copySign(evoluteCuspR + ratio * deltaR, p.getX()),
  183.                                     ratio * y);
  184.             }

  185.             // the point is on the part of the major axis within ellipse evolute
  186.             // we can compute the closest ellipse point analytically
  187.             final double rEllipse = x / e2;
  188.             return new Vector2D(FastMath.copySign(rEllipse, p.getX()),
  189.                                 FastMath.copySign(g * FastMath.sqrt(a2 - rEllipse * rEllipse), y));

  190.         } else {

  191.             // initial point at evolute cusp along major axis
  192.             double omegaX = a * e2;
  193.             double omegaY = 0.0;

  194.             double projectedX = x;
  195.             double projectedY = y;
  196.             double deltaX     = Double.POSITIVE_INFINITY;
  197.             double deltaY     = Double.POSITIVE_INFINITY;
  198.             int count = 0;
  199.             final double threshold = ANGULAR_THRESHOLD * ANGULAR_THRESHOLD * a2;
  200.             while ((deltaX * deltaX + deltaY * deltaY) > threshold && count++ < 100) { // this loop usually converges in 3 iterations

  201.                 // find point at the intersection of ellipse and line going from query point to evolute point
  202.                 final double dx         = x - omegaX;
  203.                 final double dy         = y - omegaY;
  204.                 final double alpha      = b2 * dx * dx + a2 * dy * dy;
  205.                 final double betaPrime  = b2 * omegaX * dx + a2 * omegaY * dy;
  206.                 final double gamma      = b2 * omegaX * omegaX + a2 * omegaY * omegaY - a2 * b2;
  207.                 final double deltaPrime = MathArrays.linearCombination(betaPrime, betaPrime, -alpha, gamma);
  208.                 final double ratio      = (betaPrime <= 0) ?
  209.                                           (FastMath.sqrt(deltaPrime) - betaPrime) / alpha :
  210.                                           -gamma / (FastMath.sqrt(deltaPrime) + betaPrime);
  211.                 final double previousX  = projectedX;
  212.                 final double previousY  = projectedY;
  213.                 projectedX = omegaX + ratio * dx;
  214.                 projectedY = omegaY + ratio * dy;

  215.                 // find new evolute point
  216.                 omegaX     = evoluteFactorX * projectedX * projectedX * projectedX;
  217.                 omegaY     = evoluteFactorY * projectedY * projectedY * projectedY;

  218.                 // compute convergence parameters
  219.                 deltaX     = projectedX - previousX;
  220.                 deltaY     = projectedY - previousY;

  221.             }
  222.             return new Vector2D(FastMath.copySign(projectedX, p.getX()), projectedY);
  223.         }
  224.     }

  225.     /** Project position-velocity-acceleration on an ellipse.
  226.      * @param pv position-velocity-acceleration to project, in the reference frame
  227.      * @return projected position-velocity-acceleration
  228.      */
  229.     public TimeStampedPVCoordinates projectToEllipse(final TimeStampedPVCoordinates pv) {

  230.         // find the closest point in the meridian plane
  231.         final Vector2D p2D = toPlane(pv.getPosition());
  232.         final Vector2D e2D = projectToEllipse(p2D);

  233.         // tangent to the ellipse
  234.         final double fx = -a2 * e2D.getY();
  235.         final double fy =  b2 * e2D.getX();
  236.         final double f2 = fx * fx + fy * fy;
  237.         final double f  = FastMath.sqrt(f2);
  238.         final Vector2D tangent = new Vector2D(fx / f, fy / f);

  239.         // normal to the ellipse (towards interior)
  240.         final Vector2D normal = new Vector2D(-tangent.getY(), tangent.getX());

  241.         // center of curvature
  242.         final double x2     = e2D.getX() * e2D.getX();
  243.         final double y2     = e2D.getY() * e2D.getY();
  244.         final double eX     = evoluteFactorX * x2;
  245.         final double eY     = evoluteFactorY * y2;
  246.         final double omegaX = eX * e2D.getX();
  247.         final double omegaY = eY * e2D.getY();

  248.         // velocity projection ratio
  249.         final double rho                = FastMath.hypot(e2D.getX() - omegaX, e2D.getY() - omegaY);
  250.         final double d                  = FastMath.hypot(p2D.getX() - omegaX, p2D.getY() - omegaY);
  251.         final double projectionRatio    = rho / d;

  252.         // tangential velocity
  253.         final Vector2D pDot2D           = new Vector2D(Vector3D.dotProduct(pv.getVelocity(), u),
  254.                                                        Vector3D.dotProduct(pv.getVelocity(), v));
  255.         final double   pDotTangent      = pDot2D.dotProduct(tangent);
  256.         final double   pDotNormal       = pDot2D.dotProduct(normal);
  257.         final double   eDotTangent      = projectionRatio * pDotTangent;
  258.         final Vector2D eDot2D           = new Vector2D(eDotTangent, tangent);
  259.         final Vector2D tangentDot       = new Vector2D(a2 * b2 * (e2D.getX() * eDot2D.getY() - e2D.getY() * eDot2D.getX()) / f2,
  260.                                                        normal);

  261.         // velocity of the center of curvature in the meridian plane
  262.         final double omegaXDot          = 3 * eX * eDotTangent * tangent.getX();
  263.         final double omegaYDot          = 3 * eY * eDotTangent * tangent.getY();

  264.         // derivative of the projection ratio
  265.         final double voz                = omegaXDot * tangent.getY() - omegaYDot * tangent.getX();
  266.         final double vsz                = -pDotNormal;
  267.         final double projectionRatioDot = ((rho - d) * voz - rho * vsz) / (d * d);

  268.         // acceleration
  269.         final Vector2D pDotDot2D        = new Vector2D(Vector3D.dotProduct(pv.getAcceleration(), u),
  270.                                                        Vector3D.dotProduct(pv.getAcceleration(), v));
  271.         final double   pDotDotTangent   = pDotDot2D.dotProduct(tangent);
  272.         final double   pDotTangentDot   = pDot2D.dotProduct(tangentDot);
  273.         final double   eDotDotTangent   = projectionRatio    * (pDotDotTangent + pDotTangentDot) +
  274.                                           projectionRatioDot * pDotTangent;
  275.         final Vector2D eDotDot2D        = new Vector2D(eDotDotTangent, tangent, eDotTangent, tangentDot);

  276.         // back to 3D
  277.         final Vector3D e3D       = toSpace(e2D);
  278.         final Vector3D eDot3D    = new Vector3D(eDot2D.getX(),    u, eDot2D.getY(),    v);
  279.         final Vector3D eDotDot3D = new Vector3D(eDotDot2D.getX(), u, eDotDot2D.getY(), v);

  280.         return new TimeStampedPVCoordinates(pv.getDate(), e3D, eDot3D, eDotDot3D);

  281.     }

  282.     /** Find the center of curvature (point on the evolute) at the nadir of a point.
  283.      * @param point point in the ellipse plane
  284.      * @return center of curvature of the ellipse directly at point nadir
  285.      * @since 7.1
  286.      */
  287.     public Vector2D getCenterOfCurvature(final Vector2D point) {
  288.         final Vector2D projected = projectToEllipse(point);
  289.         return new Vector2D(evoluteFactorX * projected.getX() * projected.getX() * projected.getX(),
  290.                             evoluteFactorY * projected.getY() * projected.getY() * projected.getY());
  291.     }

  292. }