/// <summary>
    /// Add an element to the sequence using r0/v0/t0 initial conditions.
    ///
    /// Position and velocity are with respect to the center body (NOT world/physics space!).
    ///
    /// Orbit segements must be added in increasing time order.
    /// </summary>
    /// <param name="r0"></param>
    /// <param name="v0"></param>
    /// <param name="time"></param>
    /// <param name="relativePos"></param>
    /// <param name="body"></param>
    /// <param name="centerBody"></param>
    /// <param name="callback">(Optional) Method to call when sequence starts</param>
    /// <returns></returns>
    public OrbitUniversal AppendElementRVT(Vector3d r0,
                                           Vector3d v0,
                                           double time,
                                           bool relativePos,
                                           NBody body,
                                           NBody centerBody,
                                           ElementStarted callback)
    {
        if (BadTime(time))
        {
            return(null);
        }
        KeplerElement ke = new KeplerElement
        {
            timeStart  = time,
            callback   = callback,
            returnToGE = false
        };
        OrbitUniversal orbit = orbitsGO.AddComponent <OrbitUniversal>();

        orbit.centerNbody = centerBody;
        orbit.SetNBody(body);
        orbit.InitFromRVT(r0, v0, time, centerBody, relativePos);
        orbit.evolveMode = OrbitUniversal.EvolveMode.KEPLERS_EQN;
        ke.orbit         = orbit;
        keplerElements.Add(ke);
        return(orbit);
    }
    /// <summary>
    /// Add an element to the sequence that begins at time and evolves based on the orbit data provided.
    ///
    /// Orbit elements must be added in increasing time order.
    /// </summary>
    /// <param name="time"></param>
    /// <param name="orbitData"></param>
    /// <param name="body"></param>
    /// <param name="centerBody"></param>
    /// <param name="callback">(Optional) Method to call when sequence starts</param>
    /// <returns></returns>
    public OrbitUniversal AppendElementOrbitData(double time,
                                                 OrbitData orbitData,
                                                 NBody body,
                                                 NBody centerBody,
                                                 ElementStarted callback)
    {
        if (BadTime(time))
        {
            return(null);
        }
        KeplerElement ke = new KeplerElement
        {
            timeStart  = time,
            returnToGE = false,
            callback   = callback
        };
        OrbitUniversal orbit = orbitsGO.AddComponent <OrbitUniversal>();

        orbit.centerNbody = centerBody;
        orbit.SetNBody(body);
        orbit.InitFromOrbitData(orbitData, time);
        orbit.evolveMode = OrbitUniversal.EvolveMode.KEPLERS_EQN;
        ke.orbit         = orbit;
        keplerElements.Add(ke);
        return(orbit);
    }
    public void AppendReturnToGE(double time, NBody body)
    {
        KeplerElement ke = new KeplerElement();

        ke.timeStart  = time;
        ke.returnToGE = true;
        keplerElements.Add(ke);
    }
    /// <summary>
    /// Add an element using an existing OrbitUniversal instance
    ///
    /// Orbit elements must be added in increasing time order.
    /// </summary>
    /// <param name="orbitU"></param>
    /// <param name="callback">(Optional) Method to call when sequence starts</param>
    public void AppendElementExistingOrbitU(OrbitUniversal orbitU, ElementStarted callback)
    {
        if (BadTime(orbitU.GetStartTime()))
        {
            return;
        }
        KeplerElement ke = new KeplerElement
        {
            timeStart  = orbitU.GetStartTime(),
            returnToGE = false
        };

        ke.orbit    = orbitU;
        ke.callback = callback;
        keplerElements.Add(ke);
    }
 /// <summary>
 /// Add orbit segements for each of the maneuvers. The maneuvers must have been created by transfer code
 /// that populated the fields: relativeTo, relativePos, relativeVel and time fields.
 ///
 /// </summary>
 /// <param name="list"></param>
 public void AddManeuvers(List <Maneuver> maneuverList)
 {
     foreach (Maneuver m in maneuverList)
     {
         if (m.relativeTo != null)
         {
             Debug.LogFormat("Maneuver: relPos={0} phyPos={1} t={2}", m.relativePos, m.physPosition, m.worldTime);
             AppendElementRVT(m.relativePos, m.relativeVel, m.worldTime, true, m.nbody, m.relativeTo, null);
             // add the maneuver to the KE so callback can be used
             KeplerElement ke = keplerElements[keplerElements.Count - 1];
             ke.maneuver = m;
         }
         else
         {
             Debug.LogError("Could not add maneuver. Missing relativeTo information. Skipped.");
         }
     }
 }
    public void Evolve(double physicsTime, ref double[] r)
    {
        if ((activeElement < keplerElements.Count - 1) &&
            (physicsTime > keplerElements[activeElement + 1].timeStart))
        {
            // Advance to next element
            activeElement++;
            GravityEngine ge       = GravityEngine.Instance();
            KeplerElement activeKE = keplerElements[activeElement];
            if (activeKE.returnToGE)
            {
#pragma warning disable 162     // disable unreachable code warning
                if (GravityEngine.DEBUG)
                {
                    Debug.Log("return to GE:" + gameObject.name);
                }
#pragma warning restore 162
                KeplerElement priorKE = keplerElements[activeElement - 1];
                priorKE.orbit.Evolve(physicsTime, ref r);
                Vector3d pos = priorKE.orbit.GetPositionDouble();
                Vector3d vel = priorKE.orbit.GetVelocityDouble();
                ge.BodyOffRails(nbody, pos, vel);
                return;
            }
            else
            {
                // if the center object of the orbit changes, need to recompute the KeplerDepth and update
                if (keplerElements[activeElement - 1].orbit.centerNbody != activeKE.orbit.centerNbody)
                {
                    NewCenter(activeKE.orbit);
                }
                // move on to the next orbit in the sequence
                activeKE.orbit.PreEvolve(ge.physToWorldFactor, ge.massScale);
                if (activeKE.callback != null)
                {
                    activeKE.callback(activeKE.orbit);
                }
                if ((activeKE.maneuver != null) && (activeKE.maneuver.onExecuted != null))
                {
                    activeKE.maneuver.onExecuted(activeKE.maneuver);
                }
            }
#pragma warning disable 162     // disable unreachable code warning
            if (GravityEngine.DEBUG)
            {
                Debug.LogFormat("Changed to segment {0} tnow={1} tseg={2} ", activeElement, physicsTime,
                                keplerElements[activeElement].timeStart);
            }
#pragma warning restore 162
        }
        else if (physicsTime < keplerElements[activeElement].timeStart)
        {
            // Use an earlier element (happens if time set to earlier)
            int lastElement = activeElement;
            while (physicsTime < keplerElements[activeElement].timeStart && (activeElement >= 0))
            {
                activeElement--;
                // if the center object of the orbit changes, need to recompute the KeplerDepth and update
                if (keplerElements[lastElement].orbit.centerNbody != keplerElements[activeElement].orbit.centerNbody)
                {
                    NewCenter(keplerElements[activeElement].orbit);
                }
            }
        }

        if (!keplerElements[activeElement].returnToGE)
        {
            // time for evolve is absolute - up to OrbitUniversal to make it relative to their time0
            keplerElements[activeElement].orbit.Evolve(physicsTime, ref r);
        }
    }