Added impact readouts.
Added impact readouts.

--- a/KerbalEngineer/Flight/Readouts/ReadoutLibrary.cs
+++ b/KerbalEngineer/Flight/Readouts/ReadoutLibrary.cs
@@ -48,8 +48,8 @@
         {

             // Orbital

             this.readoutModules.Add(new Orbital.ApoapsisHeight());

+            this.readoutModules.Add(new Orbital.PeriapsisHeight());

             this.readoutModules.Add(new Orbital.TimeToApoapsis());

-            this.readoutModules.Add(new Orbital.PeriapsisHeight());

             this.readoutModules.Add(new Orbital.TimeToPeriapsis());

             this.readoutModules.Add(new Inclination());

             this.readoutModules.Add(new Eccentricity());

@@ -70,6 +70,10 @@
             this.readoutModules.Add(new GeeForce());

             this.readoutModules.Add(new TerminalVelocity());

             this.readoutModules.Add(new AtmosphericEfficiency());

+            this.readoutModules.Add(new ImpactTime());

+            this.readoutModules.Add(new ImpactLongitude());

+            this.readoutModules.Add(new ImpactLatitude());

+            this.readoutModules.Add(new ImpactAltitude());

 

             // Vessel

             this.readoutModules.Add(new DeltaVStaged());

@@ -88,8 +92,8 @@
             this.readoutModules.Add(new AngleToDescendingNode());

             this.readoutModules.Add(new Rendezvous.AltitudeSeaLevel());

             this.readoutModules.Add(new Rendezvous.ApoapsisHeight());

+            this.readoutModules.Add(new Rendezvous.PeriapsisHeight());

             this.readoutModules.Add(new Rendezvous.TimeToApoapsis());

-            this.readoutModules.Add(new Rendezvous.PeriapsisHeight());

             this.readoutModules.Add(new Rendezvous.TimeToPeriapsis());

             this.readoutModules.Add(new Distance());

             this.readoutModules.Add(new Rendezvous.OrbitalPeriod());


--- /dev/null
+++ b/KerbalEngineer/Flight/Readouts/Surface/ImpactAltitude.cs
@@ -1,1 +1,44 @@
-
+// Project:	KerbalEngineer

+// Author:	CYBUTEK

+// License:	Attribution-NonCommercial-ShareAlike 3.0 Unported

+

+using KerbalEngineer.Extensions;

+

+namespace KerbalEngineer.Flight.Readouts.Surface

+{

+    public class ImpactAltitude : ReadoutModule

+    {

+        private bool showing;

+

+        public ImpactAltitude()

+        {

+            this.Name = "Impact Altitude";

+            this.Category = ReadoutCategory.Surface;

+            //this.HelpString = "";

+        }

+

+        public override void Update()

+        {

+            ImpactProcessor.RequestUpdate();

+        }

+

+        public override void Draw()

+        {

+            if (ImpactProcessor.ShowDetails)

+            {

+                this.showing = true;

+                this.DrawLine(ImpactProcessor.Altitude.ToAngle());

+            }

+            else if (this.showing)

+            {

+                this.showing = false;

+                this.ResizeRequested = true;

+            }

+        }

+

+        public override void Reset()

+        {

+            FlightEngineerCore.Instance.AddUpdatable(ImpactProcessor.Instance);

+        }

+    }

+}

--- /dev/null
+++ b/KerbalEngineer/Flight/Readouts/Surface/ImpactLatitude.cs
@@ -1,1 +1,44 @@
-
+// Project:	KerbalEngineer

+// Author:	CYBUTEK

+// License:	Attribution-NonCommercial-ShareAlike 3.0 Unported

+

+using KerbalEngineer.Extensions;

+

+namespace KerbalEngineer.Flight.Readouts.Surface

+{

+    public class ImpactLatitude : ReadoutModule

+    {

+        private bool showing;

+

+        public ImpactLatitude()

+        {

+            this.Name = "Impact Latitude";

+            this.Category = ReadoutCategory.Surface;

+            //this.HelpString = "";

+        }

+

+        public override void Update()

+        {

+            ImpactProcessor.RequestUpdate();

+        }

+

+        public override void Draw()

+        {

+            if (ImpactProcessor.ShowDetails)

+            {

+                this.showing = true;

+                this.DrawLine(ImpactProcessor.Latitude.ToAngle());

+            }

+            else if (this.showing)

+            {

+                this.showing = false;

+                this.ResizeRequested = true;

+            }

+        }

+

+        public override void Reset()

+        {

+            FlightEngineerCore.Instance.AddUpdatable(ImpactProcessor.Instance);

+        }

+    }

+}

--- /dev/null
+++ b/KerbalEngineer/Flight/Readouts/Surface/ImpactLongitude.cs
@@ -1,1 +1,44 @@
-
+// Project:	KerbalEngineer

+// Author:	CYBUTEK

+// License:	Attribution-NonCommercial-ShareAlike 3.0 Unported

+

+using KerbalEngineer.Extensions;

+

+namespace KerbalEngineer.Flight.Readouts.Surface

+{

+    public class ImpactLongitude : ReadoutModule

+    {

+        private bool showing;

+

+        public ImpactLongitude()

+        {

+            this.Name = "Impact Longitude";

+            this.Category = ReadoutCategory.Surface;

+            //this.HelpString = "";

+        }

+

+        public override void Update()

+        {

+            ImpactProcessor.RequestUpdate();

+        }

+

+        public override void Draw()

+        {

+            if (ImpactProcessor.ShowDetails)

+            {

+                this.showing = true;

+                this.DrawLine(ImpactProcessor.Longitude.ToAngle());

+            }

+            else if (this.showing)

+            {

+                this.showing = false;

+                this.ResizeRequested = true;

+            }

+        }

+

+        public override void Reset()

+        {

+            FlightEngineerCore.Instance.AddUpdatable(ImpactProcessor.Instance);

+        }

+    }

+}

--- /dev/null
+++ b/KerbalEngineer/Flight/Readouts/Surface/ImpactProcessor.cs
@@ -1,1 +1,257 @@
-
+// The calculation code for this processor was provided by mic_e.

+

+#region

+

+using System;

+

+using UnityEngine;

+

+#endregion

+

+namespace KerbalEngineer.Flight.Readouts.Surface

+{

+    public class ImpactProcessor : IUpdatable, IUpdateRequest

+    {

+        #region Instance

+

+        private static readonly ImpactProcessor instance = new ImpactProcessor();

+

+        /// <summary>

+        ///     Gets the current instance of the impact processor.

+        /// </summary>

+        public static ImpactProcessor Instance

+        {

+            get { return instance; }

+        }

+

+        #endregion

+

+        #region Fields

+

+        private double impactAltitude;

+        private bool impactHappening;

+        private double impactLatitude;

+        private double impactLongitude;

+        private double impactTime;

+

+        #endregion

+

+        #region Properties

+

+        /// <summary>

+        ///     Gets whether the details are ready to be shown.

+        /// </summary>

+        public static bool ShowDetails { get; private set; }

+

+        /// <summary>

+        ///     Gets the time to impact.

+        /// </summary>

+        public static double Time { get; private set; }

+

+        /// <summary>

+        ///     Gets the longitude of the impact coordinates.

+        /// </summary>

+        public static double Longitude { get; private set; }

+

+        /// <summary>

+        ///     Gets the latitude of the impact coordinates.

+        /// </summary>

+        public static double Latitude { get; private set; }

+

+        /// <summary>

+        ///     Gets the altitude of the impact coordinates.

+        /// </summary>

+        public static double Altitude { get; private set; }

+

+        #endregion

+

+        #region IUpdatable Members

+

+        public void Update()

+        {

+            this.impactHappening = false;

+

+            if (FlightGlobals.ActiveVessel.mainBody.pqsController != null)

+            {

+                //do impact site calculations

+                this.impactHappening = true;

+                this.impactTime = 0;

+                this.impactLongitude = 0;

+                this.impactLatitude = 0;

+                this.impactAltitude = 0;

+                var e = FlightGlobals.ActiveVessel.orbit.eccentricity;

+                //get current position direction vector

+                var currentpos = this.RadiusDirection(FlightGlobals.ActiveVessel.orbit.trueAnomaly);

+                //calculate longitude in inertial reference frame from that

+                var currentirflong = 180 * Math.Atan2(currentpos.x, currentpos.y) / Math.PI;

+

+                //experimentally determined; even for very flat trajectories, the errors go into the sub-millimeter area after 5 iterations or so

+                const int impactiterations = 6;

+

+                //do a few iterations of impact site calculations

+                for (var i = 0; i < impactiterations; i++)

+                {

+                    if (FlightGlobals.ActiveVessel.orbit.PeA >= this.impactAltitude)

+                    {

+                        //periapsis must be lower than impact alt

+                        this.impactHappening = false;

+                    }

+                    if ((FlightGlobals.ActiveVessel.orbit.eccentricity < 1) && (FlightGlobals.ActiveVessel.orbit.ApA <= this.impactAltitude))

+                    {

+                        //apoapsis must be higher than impact alt

+                        this.impactHappening = false;

+                    }

+                    if ((FlightGlobals.ActiveVessel.orbit.eccentricity >= 1) && (FlightGlobals.ActiveVessel.orbit.timeToPe <= 0))

+                    {

+                        //if currently escaping, we still need to be before periapsis

+                        this.impactHappening = false;

+                    }

+                    if (!this.impactHappening)

+                    {

+                        this.impactTime = 0;

+                        this.impactLongitude = 0;

+                        this.impactLatitude = 0;

+                        this.impactAltitude = 0;

+                        break;

+                    }

+

+                    double impacttheta = 0;

+                    if (e > 0)

+                    {

+                        //in this step, we are using the calculated impact altitude of the last step, to refine the impact site position

+                        impacttheta = -180 * Math.Acos((FlightGlobals.ActiveVessel.orbit.PeR * (1 + e) / (FlightGlobals.ActiveVessel.mainBody.Radius + this.impactAltitude) - 1) / e) / Math.PI;

+                    }

+

+                    //calculate time to impact

+                    this.impactTime = FlightGlobals.ActiveVessel.orbit.timeToPe - this.TimeToPeriapsis(impacttheta);

+                    //calculate position vector of impact site

+                    var impactpos = this.RadiusDirection(impacttheta);

+                    //calculate longitude of impact site in inertial reference frame

+                    var impactirflong = 180 * Math.Atan2(impactpos.x, impactpos.y) / Math.PI;

+                    var deltairflong = impactirflong - currentirflong;

+                    //get body rotation until impact

+                    var bodyrot = 360 * this.impactTime / FlightGlobals.ActiveVessel.mainBody.rotationPeriod;

+                    //get current longitude in body coordinates

+                    var currentlong = FlightGlobals.ActiveVessel.longitude;

+                    //finally, calculate the impact longitude in body coordinates

+                    this.impactLongitude = this.NormAngle(currentlong - deltairflong - bodyrot);

+                    //calculate impact latitude from impact position

+                    this.impactLatitude = 180 * Math.Asin(impactpos.z / impactpos.magnitude) / Math.PI;

+                    //calculate the actual altitude of the impact site

+                    //altitude for long/lat code stolen from some ISA MapSat forum post; who knows why this works, but it seems to.

+                    var rad = QuaternionD.AngleAxis(this.impactLongitude, Vector3d.down) * QuaternionD.AngleAxis(this.impactLatitude, Vector3d.forward) * Vector3d.right;

+                    this.impactAltitude = FlightGlobals.ActiveVessel.mainBody.pqsController.GetSurfaceHeight(rad) - FlightGlobals.ActiveVessel.mainBody.pqsController.radius;

+                    if ((this.impactAltitude < 0) && FlightGlobals.ActiveVessel.mainBody.ocean)

+                    {

+                        this.impactAltitude = 0;

+                    }

+                }

+            }

+

+            // Set accessable properties.

+            if (this.impactHappening)

+            {

+                ShowDetails = true;

+                Time = this.impactTime;

+                Longitude = this.impactLongitude;

+                Latitude = this.impactLatitude;

+                Altitude = this.impactAltitude;

+            }

+            else

+            {

+                ShowDetails = false;

+            }

+        }

+

+        #endregion

+

+        #region IUpdateRequest Members

+

+        public bool UpdateRequested { get; set; }

+

+        #endregion

+

+        public static void RequestUpdate()

+        {

+            instance.UpdateRequested = true;

+        }

+

+        #region Calculations

+

+        private double NormAngle(double ang)

+        {

+            if (ang > 180)

+            {

+                ang -= 360 * Math.Ceiling((ang - 180) / 360);

+            }

+            if (ang <= -180)

+            {

+                ang -= 360 * Math.Floor((ang + 180) / 360);

+            }

+

+            return ang;

+        }

+

+        private Vector3d RadiusDirection(double theta)

+        {

+            theta = Math.PI * theta / 180;

+            var omega = Math.PI * FlightGlobals.ActiveVessel.orbit.argumentOfPeriapsis / 180;

+            var incl = Math.PI * FlightGlobals.ActiveVessel.orbit.inclination / 180;

+

+            var costheta = Math.Cos(theta);

+            var sintheta = Math.Sin(theta);

+            var cosomega = Math.Cos(omega);

+            var sinomega = Math.Sin(omega);

+            var cosincl = Math.Cos(incl);

+            var sinincl = Math.Sin(incl);

+

+            Vector3d result;

+

+            result.x = cosomega * costheta - sinomega * sintheta;

+            result.y = cosincl * (sinomega * costheta + cosomega * sintheta);

+            result.z = sinincl * (sinomega * costheta + cosomega * sintheta);

+

+            return result;

+        }

+

+        public static double ACosh(double x)

+        {

+            return (Math.Log(x + Math.Sqrt((x * x) - 1.0)));

+        }

+

+        private double TimeToPeriapsis(double theta)

+        {

+            var e = FlightGlobals.ActiveVessel.orbit.eccentricity;

+            var a = FlightGlobals.ActiveVessel.orbit.semiMajorAxis;

+            var rp = FlightGlobals.ActiveVessel.orbit.PeR;

+            var mu = FlightGlobals.ActiveVessel.mainBody.gravParameter;

+

+            if (e == 1.0)

+            {

+                var D = Math.Tan(Math.PI * theta / 360.0);

+                var M = D + D * D * D / 3.0;

+                return (Math.Sqrt(2.0 * rp * rp * rp / mu) * M);

+            }

+            if (a > 0)

+            {

+                var cosTheta = Math.Cos(Math.PI * theta / 180.0);

+                var cosE = (e + cosTheta) / (1.0 + e * cosTheta);

+                var radE = Math.Acos(cosE);

+                var M = radE - e * Math.Sin(radE);

+                return (Math.Sqrt(a * a * a / mu) * M);

+            }

+            if (a < 0)

+            {

+                var cosTheta = Math.Cos(Math.PI * theta / 180.0);

+                var coshF = (e + cosTheta) / (1.0 + e * cosTheta);

+                var radF = ACosh(coshF);

+                var M = e * Math.Sinh(radF) - radF;

+                return (Math.Sqrt(-a * a * a / mu) * M);

+            }

+

+            return 0;

+        }

+

+        #endregion

+    }

+}

--- /dev/null
+++ b/KerbalEngineer/Flight/Readouts/Surface/ImpactTime.cs
@@ -1,1 +1,44 @@
-
+// Project:	KerbalEngineer

+// Author:	CYBUTEK

+// License:	Attribution-NonCommercial-ShareAlike 3.0 Unported

+

+using KerbalEngineer.Extensions;

+

+namespace KerbalEngineer.Flight.Readouts.Surface

+{

+    public class ImpactTime : ReadoutModule

+    {

+        private bool showing;

+

+        public ImpactTime()

+        {

+            this.Name = "Impact Time";

+            this.Category = ReadoutCategory.Surface;

+            //this.HelpString = "";

+        }

+

+        public override void Update()

+        {

+            ImpactProcessor.RequestUpdate();

+        }

+

+        public override void Draw()

+        {

+            if (ImpactProcessor.ShowDetails)

+            {

+                this.showing = true;

+                this.DrawLine(ImpactProcessor.Time.ToTime());

+            }

+            else if (this.showing)

+            {

+                this.showing = false;

+                this.ResizeRequested = true;

+            }

+        }

+

+        public override void Reset()

+        {

+            FlightEngineerCore.Instance.AddUpdatable(ImpactProcessor.Instance);

+        }

+    }

+}

--- a/KerbalEngineer/KerbalEngineer.csproj
+++ b/KerbalEngineer/KerbalEngineer.csproj
@@ -99,10 +99,15 @@
     <Compile Include="Flight\Readouts\Rendezvous\TargetSelector.cs" />

     <Compile Include="Flight\Readouts\Surface\AltitudeSeaLevel.cs" />

     <Compile Include="Flight\Readouts\Surface\AltitudeTerrain.cs" />

+    <Compile Include="Flight\Readouts\Surface\ImpactLatitude.cs" />

+    <Compile Include="Flight\Readouts\Surface\ImpactAltitude.cs" />

+    <Compile Include="Flight\Readouts\Surface\ImpactLongitude.cs" />

+    <Compile Include="Flight\Readouts\Surface\ImpactTime.cs" />

     <Compile Include="Flight\Readouts\Surface\AtmosphericProcessor.cs" />

     <Compile Include="Flight\Readouts\Surface\AtmosphericEfficiency.cs" />

     <Compile Include="Flight\Readouts\Surface\GeeForce.cs" />

     <Compile Include="Flight\Readouts\Surface\HorizontalSpeed.cs" />

+    <Compile Include="Flight\Readouts\Surface\ImpactProcessor.cs" />

     <Compile Include="Flight\Readouts\Surface\Latitude.cs" />

     <Compile Include="Flight\Readouts\Surface\Longitude.cs" />

     <Compile Include="Flight\Readouts\Surface\TerminalVelocity.cs" />


 Binary files a/Output/KerbalEngineer/KerbalEngineer.dll and b/Output/KerbalEngineer/KerbalEngineer.dll differ