<|start_filename|>src/SpaceSim/SolarSystem/Moons/Callisto.cs<|end_filename|>
﻿using System.Drawing;
using SpaceSim.Kernels;
using SpaceSim.Orbits;
using VectorMath;

namespace SpaceSim.SolarSystem.Moons
{
    class Callisto : MassiveBodyBase
    {
        public override double Mass
        {
            get { return 1.075938e23; }
        }

        public override double SurfaceRadius
        {
            get { return BaseKernel.CALLISTO_RADIUS; }
        }

        public override double AtmosphereHeight
        {
            get { return 0.0; }
        }

        public override double RotationRate
        {
            get { return -4.3574794015100468486813972729667e-6; }
        }

        public override double RotationPeriod
        {
            get { return 1441931.1552; }
        }

        public override Color IconColor { get { return Color.RosyBrown; } }
        public override Color IconAtmopshereColor { get { return Color.White; } }

        public Callisto(DVector2 parentPositon, DVector2 parentVelocity)
            : base(OrbitHelper.FromJplEphemeris(-1.838974425381978E+06, 4.612334321076249E+05) + parentPositon,
                OrbitHelper.FromJplEphemeris(-1.996468410126901E+00, -7.894863713236139E+00) + parentVelocity, new CallistoKernel())
        {
        }

        public override double GetAtmosphericDensity(double height)
        {
            return 0.0;
        }

        public override string ToString()
        {
            return "Callisto";
        }
    }
}


<|start_filename|>src/SpaceSim/Structures/Ocean.cs<|end_filename|>
﻿using System.Drawing;
using SpaceSim.SolarSystem;

namespace SpaceSim.Structures
{
    class Ocean : StructureBase
    {
        public override double Width { get { return 500; } }
        public override double Height { get { return 100; } }

        public override Color IconColor { get { return Color.DeepSkyBlue; } }

        public Ocean(double surfaceAngle, double height, IMassiveBody parent)
            : base(surfaceAngle, height, "Textures/Structures/ocean.png", parent)
        {
        }
    }
}


<|start_filename|>src/SpaceSim/Gauges/RetrogradeButton.cs<|end_filename|>
﻿using SpaceSim.Properties;
using System.Drawing;
using VectorMath;

namespace SpaceSim.Gauges
{
    class RetrogradeButton : IGauge
    {
        public bool IsActive { get; private set; }

        public RectangleF Bounds { get; private set; }

        private Point _center;
        private Font _font;
        private float _size;

        public RetrogradeButton(Point center)
        {
            _center = center;
            _font = Settings.Default.Font;
            _size = _font.Size;
            Bounds = new RectangleF(_center.X - _size, _center.Y - _size, _size * 2, _size * 2);
        }

        public void Enable()
        {
            IsActive = true;
        }

        public void Disable()
        {
            IsActive = false;
        }

        public void Update(double thrustAngle, double thrustMagnitude, double flightPathAngle) { }

        public void Render(Graphics graphics, RectangleD cameraBounds)
        {
            if (IsActive)
            {
                graphics.FillEllipse(new SolidBrush(Color.Red), Bounds);
            }

            graphics.DrawString("R", _font, new SolidBrush(Color.White), _center.X - _size * 0.6f, _center.Y - _size * 0.8f);
            graphics.DrawEllipse(new Pen(Color.White, 2), Bounds);
        }
    }
}


<|start_filename|>src/SpaceSim/Spacecrafts/Electron/ElectronFairing.cs<|end_filename|>
﻿using System.Drawing;
using SpaceSim.Spacecrafts;
using VectorMath;

using System;
using SpaceSim.Physics;
using SpaceSim.Engines;
using SpaceSim.Drawing;

namespace SpaceSim.Spacecrafts.Electron
{
    class ElectronFairing : SpaceCraftBase
    {
        public override Color IconColor { get { return Color.White; } }

        public override double Width { get { return 0.6; } }
        public override double Height { get { return 2.5; } }

        public ElectronFairing(string craftDirectory, DVector2 position, DVector2 velocity, bool isLeft)
            : base(craftDirectory, position, velocity, 0, 0, isLeft ? "Electron/electronLeft.png" : "Electron/electronRight.png")
        {
            _isLeft = isLeft;

            if (_isLeft)
            {
                StageOffset = new DVector2(-0.3, -1.0);
            }
            else
            {
                StageOffset = new DVector2(0.3, -1.0);
            }

            Engines = new IEngine[0];
        }

        public override string CraftName { get { return _isLeft ? "Fairing Left" : "Fairing Right"; } }
        public override string CommandFileName { get { return _isLeft ? "fairingLeft.xml" : "fairingRight.xml"; } }

        public override double DryMass { get { return 25; } }

        public override AeroDynamicProperties GetAeroDynamicProperties
        {
            get { return AeroDynamicProperties.ExposedToAirFlow; }
        }

        public override double StagingForce
        {
            get { return 1000; }
        }

        public override double FormDragCoefficient
        {
            get
            {
                double baseCd = GetBaseCd(0.4);
                double alpha = GetAlpha();
                return baseCd * Math.Cos(alpha);
            }
        }

        public override double LiftCoefficient
        {
            get
            {
                double baseCd = GetBaseCd(0.6);
                double alpha = GetAlpha();
                return baseCd * Math.Sin(alpha * 2.0);
            }
        }

        public override double FrontalArea { get { return Math.PI * Math.Pow(Width / 2, 2); } }
        public override double ExposedSurfaceArea { get { return 2 * Math.PI * (Width / 2) * Height + FrontalArea; } }
        public override double LiftingSurfaceArea { get { return Width * Height; } }

        private bool _isLeft;
        private bool _isHidden;

        public void Hide()
        {
            _isHidden = true;
        }

        public override void RenderGdi(Graphics graphics, Camera camera)
        {
            if (_isHidden) return;

            base.RenderGdi(graphics, camera);
        }
    }
}


<|start_filename|>src/SpaceSim/Spacecrafts/Falcon9/F9S2B.cs<|end_filename|>
﻿using System;
using System.Drawing;
using SpaceSim.Engines;
using SpaceSim.Physics;
using VectorMath;

using SpaceSim.Drawing;
using SpaceSim.Spacecrafts.FalconCommon;
using SpaceSim.Properties;
using System.IO;

namespace SpaceSim.Spacecrafts.Falcon9
{
    sealed class F9S2B : SpaceCraftBase
    {
        public override string CraftName { get { return "F9 S2"; } }
        public override string CommandFileName { get { return "F9S2.xml"; } }

        public override double DryMass { get { return 5000; } }

        public override double Width { get { return 5; } }
        public override double Height { get { return 16; } }

        public override AeroDynamicProperties GetAeroDynamicProperties { get { return AeroDynamicProperties.ExtendsFineness; } }

        public override double FormDragCoefficient
        {
            get
            {
                double baseCd = GetBaseCd(0.5);
                if (_drogueChute.IsDeployed())
                    baseCd = GetBaseCd(0.65);
                if (_parachute.IsDeployed())
                    baseCd = GetBaseCd(1.0);

                double alpha = GetAlpha();

                return Math.Abs(baseCd * Math.Cos(alpha));
            }
        }

        public override double LiftCoefficient
        {
            get
            {
                double baseCd = GetBaseCd(0.6);
                if (_parachute.IsDeployed())
                    baseCd = GetBaseCd(3.0);

                double alpha = GetAlpha();

                return baseCd * Math.Sin(alpha * 2);
            }
        }

        public override double FrontalArea { get { return Math.PI * Math.Pow(Width / 2, 2); } }

        public override double ExposedSurfaceArea
        {
            get
            {
                // A = 2πrh + πr2
                return 2 * Math.PI * (Width / 2.0) * Height + FrontalArea;
            }
        }

        public override double LiftingSurfaceArea { get { return Width * Height; } }

        public override Color IconColor { get { return Color.White; } }

        public override void DeployDrogues()
        {
            _drogueChute.Deploy();
        }

        public override void DeployParachutes()
        {
            _parachute.Deploy();
        }

        public override void DeployLandingLegs()
        {
            foreach (Skid skid in _skids)
            {
                skid.Deploy();
            }
        }

        private SpriteSheet _spriteSheet;
        DrogueChute _drogueChute;
        Parafoil _parachute;
        Skid[] _skids;
        DateTime timestamp = DateTime.Now;

        public F9S2B(string craftDirectory, DVector2 position, DVector2 velocity, double stageOffset, double propellantMass = 103500)
            : base(craftDirectory, position, velocity, 0, propellantMass, "Falcon/9/S2B.png")
        {
            StageOffset = new DVector2(0, stageOffset);

            _drogueChute = new DrogueChute(this, new DVector2(-2.5, 5));
            _parachute = new Parafoil(this, new DVector2(-1.5, 0.0), true);

            Engines = new IEngine[]
            {
                new Merlin1DVac(this, new DVector2(0, Height * 0.38))
            };

            _skids = new[]
            {
                new Skid(this, new DVector2(1.1, -5), true),
                new Skid(this, new DVector2(1.1, 0.5), true)
            };

            _spriteSheet = new SpriteSheet("Textures/Spacecrafts/Falcon/9/scaledShip.png", 12, 12);
        }

        public override void Update(double dt)
        {
            base.Update(dt);

            _drogueChute.Update(dt);
            _parachute.Update(dt);

            foreach (Skid skid in _skids)
            {
                skid.Update(dt);
            }
        }

        protected override void RenderShip(Graphics graphics, Camera camera, RectangleF screenBounds)
        {
            foreach (Skid skid in _skids)
            {
                if (skid.IsDeploying() || skid.IsDeployed())
                    skid.RenderGdi(graphics, camera);
            }

            double drawingRotation = Pitch + Math.PI * 0.5;

            var offset = new PointF(screenBounds.X + screenBounds.Width * 0.5f,
                                    screenBounds.Y + screenBounds.Height * 0.5f);

            graphics.TranslateTransform(offset.X, offset.Y);

            float pitchAngle = (float)(drawingRotation * 180 / Math.PI);
            float rollFactor = (float)Math.Cos(Roll);
            float alphaAngle = (float)(GetAlpha() * 180 / Math.PI);
            float rotateAngle = (pitchAngle - alphaAngle) + alphaAngle * rollFactor;

            if (this.MissionName.Contains("EDL") || this.MissionName.Contains("Aerocapture") || this.MissionName.Contains("Direct"))
                graphics.RotateTransform(rotateAngle);
            else
                graphics.RotateTransform(pitchAngle);

            graphics.TranslateTransform(-offset.X, -offset.Y);

            // Normalize the angle to [0,360]
            int rollAngle = (int)(Roll * MathHelper.RadiansToDegrees) % 360;

            int heatingRate = Math.Min((int)this.HeatingRate, 2000000);
            if (heatingRate > 100000)
            {
                Random rnd = new Random();
                float noise = (float)rnd.NextDouble();
                float width = screenBounds.Width / (3 + noise);
                float height = screenBounds.Height / (18 + noise);
                RectangleF plasmaRect = screenBounds;
                plasmaRect.Inflate(new SizeF(width, height));

                int alpha = Math.Min(heatingRate / 7800, 255);
                int red = alpha;
                int green = Math.Max(red - 128, 0) * 2;
                int blue = 0;
                Color glow = Color.FromArgb(alpha, red, green, blue);

                float penWidth = width / 12;
                Pen glowPen = new Pen(glow, penWidth);
                glowPen.StartCap = System.Drawing.Drawing2D.LineCap.Round;
                glowPen.EndCap = System.Drawing.Drawing2D.LineCap.Round;
                graphics.DrawArc(glowPen, plasmaRect, 220, 100);

                glowPen.Color = Color.FromArgb((int)(alpha * 0.75), glow);
                plasmaRect.Inflate(-penWidth, -penWidth);
                graphics.DrawArc(glowPen, plasmaRect, 200, 140);

                glowPen.Color = Color.FromArgb((int)(alpha * 0.5), glow);
                plasmaRect.Inflate(-penWidth, -penWidth);
                graphics.DrawArc(glowPen, plasmaRect, 180, 180);

                glowPen.Color = Color.FromArgb((int)(alpha * 0.25), glow);
                plasmaRect.Inflate(-penWidth, -penWidth);
                graphics.DrawArc(glowPen, plasmaRect, 160, 220);
            }

            // Index into the sprite
            int ships = _spriteSheet.Cols * _spriteSheet.Rows;
            int spriteIndex = (rollAngle * ships) / 360;
            while (spriteIndex < 0)
                spriteIndex += ships;

            _spriteSheet.Draw(spriteIndex, graphics, screenBounds);

            graphics.ResetTransform();

            if (_drogueChute.IsDeploying() || _drogueChute.IsDeployed())
                _drogueChute.RenderGdi(graphics, camera);

            if (_parachute.IsDeploying() || _parachute.IsDeployed())
                _parachute.RenderGdi(graphics, camera);

            if (Settings.Default.WriteCsv && (DateTime.Now - timestamp > TimeSpan.FromSeconds(1)))
            {
                string filename = MissionName + ".csv";

                if (!File.Exists(filename))
                {
                    File.AppendAllText(filename, "Velocity, Acceleration, Altitude, Alpha\r\n");
                }

                timestamp = DateTime.Now;

                string contents = string.Format("{0}, {1}, {2}, {3}\r\n",
                    this.GetRelativeVelocity().Length(),
                    this.GetRelativeAcceleration().Length() * 100,
                    this.GetRelativeAltitude() / 10,
                    this.GetAlpha() * 10);
                File.AppendAllText(filename, contents);
            }
        }
    }
}


<|start_filename|>src/SpaceSim/Spacecrafts/ITS/StarKicker.cs<|end_filename|>
﻿using System;
using System.Drawing;
using SpaceSim.Engines;
using SpaceSim.Physics;
using VectorMath;
using System.IO;
using SpaceSim.Common;
using SpaceSim.Drawing;
using SpaceSim.Properties;

namespace SpaceSim.Spacecrafts.ITS
{
    class StarKicker : SpaceCraftBase
    {
        public override string CraftName { get { return "StarKicker"; } }
        public override string CommandFileName { get { return "StarKicker.xml"; } }

        public override double DryMass { get { return 39750; } }
        public override double Width { get { return 9; } }
        public override double Height { get { return 36; } }

        public override AeroDynamicProperties GetAeroDynamicProperties { get { return AeroDynamicProperties.ExposedToAirFlow; } }

        DateTime timestamp = DateTime.Now;
        double payloadMass = 0;

        public override double LiftingSurfaceArea { get { return Math.Abs(Width * Height * Math.Cos(GetAlpha())); } }

        public override double LiftCoefficient
        {
            get
            {
                double baseCd = GetBaseCd(0.6);
                double alpha = GetAlpha();

                return baseCd * Math.Sin(alpha * 2);
            }
        }

        public override double FrontalArea
        {
            get
            {
                double alpha = GetAlpha();
                double crossSectionalArea = Math.PI * Math.Pow(Width / 2, 2);
                double sideArea = Width * Height;

                return Math.Abs(crossSectionalArea * Math.Cos(alpha)) + Math.Abs(sideArea * Math.Sin(alpha));
            }
        }

        public override double ExposedSurfaceArea
        {
            get
            {
                // A = 2πrh + πr2
                return 2 * Math.PI * (Width / 2) * Height + FrontalArea;
            }
        }

        public override Color IconColor
        {
            get { return Color.White; }
        }

        public override double FormDragCoefficient
        {
            get
            {
                double alpha = GetAlpha();
                double baseCd = GetBaseCd(0.5);
                bool isRetrograde = false;

                if (alpha > Constants.PiOverTwo || alpha < -Constants.PiOverTwo)
                {
                    baseCd = GetBaseCd(0.7);
                    isRetrograde = true;
                }

                double dragCoefficient = Math.Abs(baseCd * Math.Sin(alpha));

                double dragPreservation = 1.0;

                if (isRetrograde)
                {
                    // if retrograde
                    if (Throttle > 0 && MachNumber > 1.5 && MachNumber < 20.0)
                    {
                        double throttleFactor = Throttle / 50;
                        double cantFactor = Math.Sin(Engines[0].Cant * 2);
                        dragPreservation += throttleFactor * cantFactor;
                        dragCoefficient *= dragPreservation;
                    }
                }

                return Math.Abs(dragCoefficient);
            }
        }

        //private SpriteSheet _spriteSheet;

        public StarKicker(string craftDirectory, DVector2 position, DVector2 velocity, double payloadMass = 0, double propellantMass = 1160000)
            : base(craftDirectory, position, velocity, payloadMass, propellantMass, null)
        {
            StageOffset = new DVector2(4, -15);

            Engines = new IEngine[3];
            for (int i = 0; i < 3; i++)
            {
                double engineOffsetX = (i - 1) / 1.4;
                var offset = new DVector2(engineOffsetX * Width * 0.22, Height * 0.32);
                Engines[i] = new RaptorVac300(i, this, offset);
            }

            //_spriteSheet = new SpriteSheet("Textures/Spacecrafts/Its/scaledShip.png", 12, 12);

            string texturePath = "Its/StarKicker.png";
            string fullPath = Path.Combine("Textures/Spacecrafts", texturePath);
            this.Texture = new Bitmap(fullPath);

            this.payloadMass = payloadMass;
        }

        protected override void RenderShip(Graphics graphics, Camera camera, RectangleF screenBounds)
        {
            double drawingRotation = Pitch + Math.PI * 0.5;

            var offset = new PointF(screenBounds.X + screenBounds.Width * 0.5f,
                                    screenBounds.Y + screenBounds.Height * 0.5f);

            camera.ApplyScreenRotation(graphics);
            camera.ApplyRotationMatrix(graphics, offset, drawingRotation);

            // Normalize the angle to [0,360]
            int rollAngle = (int)(Roll * MathHelper.RadiansToDegrees) % 360;

            if(rollAngle <= 90)
                graphics.DrawImage(this.Texture, screenBounds.X - screenBounds.Width * 0.43f, screenBounds.Y, screenBounds.Width * 1.8f, screenBounds.Height);
            else
                graphics.DrawImage(this.Texture, screenBounds.X + screenBounds.Width * 0.9f, screenBounds.Y, -screenBounds.Width * 1.8f, screenBounds.Height);

            // Index into the sprite
            //int ships = _spriteSheet.Cols * _spriteSheet.Rows;
            //int spriteIndex = (rollAngle * ships) / 360;
            //while (spriteIndex < 0)
            //    spriteIndex += ships;

            //_spriteSheet.Draw(spriteIndex, graphics, screenBounds);

            graphics.ResetTransform();

            //if (Settings.Default.WriteCsv && (DateTime.Now - timestamp > TimeSpan.FromSeconds(1)))
            //{
            //    string filename = MissionName + ".csv";

            //    if (!File.Exists(filename))
            //    {
            //        File.AppendAllText(filename, "Velocity, Acceleration, Altitude, Throttle\r\n");
            //    }

            //    timestamp = DateTime.Now;

            //    double density = this.GravitationalParent.GetAtmosphericDensity(this.GetRelativeAltitude());
            //    double velocity = this.GetRelativeVelocity().Length();
            //    double dynamicPressure = 0.5 * density * velocity * velocity;

            //    string contents = string.Format("{0}, {1}, {2}, {3}\r\n",
            //        this.GetRelativeVelocity().Length() * 10,
            //        this.GetRelativeAcceleration().Length() * 100,
            //        this.GetRelativeAltitude() / 10,
            //        this.Throttle * 10);
            //    File.AppendAllText(filename, contents);
            //}
        }
    }
}



<|start_filename|>src/SpaceSim/Orbits/OrbitHelper.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using SpaceSim.Common;
using SpaceSim.Proxies;
using SpaceSim.SolarSystem;
using SpaceSim.Spacecrafts;
using VectorMath;

namespace SpaceSim.Orbits
{
    public delegate void SimulateProgressCallback(double progress);

    static class OrbitHelper
    {
        private static double AngularCutoff = Constants.TwoPi - 0.01;

        /// <summary>
        /// Converts orbital data from JPL Emphemeris data.
        /// </summary>
        public static DVector2 FromJplEphemeris(double x, double y)
        {
            return new DVector2(x, -y) * 1000;
        }

        public static void SimulateToTime(List<IMassiveBody> bodies, DateTime targetDate, double timeStep, SimulateProgressCallback callback)
        {
            if (targetDate < Constants.Epoch)
            {
                throw new Exception("Starting date must be greater than the epoch: " + Constants.Epoch.ToLongDateString());
            }

            TimeSpan timeToSimulate = targetDate - Constants.Epoch;

            int iterations = (int)(timeToSimulate.TotalSeconds / timeStep);

            int progressCounter = 0;
            int progressIterationStep = iterations / 100;

            for (int i = 0; i < iterations; i++)
            {
                // Update the progress ever unit of iterations
                if (i % progressIterationStep == 0)
                {
                    progressCounter++;
                    callback(progressCounter * 0.6 + 20);
                }

                // Resolve n body massive body forces
                foreach (IMassiveBody bodyA in bodies)
                {
                    bodyA.ResetAccelerations();

                    foreach (IMassiveBody bodyB in bodies)
                    {
                        if (bodyA == bodyB) continue;

                        bodyA.ResolveGravitation(bodyB);
                    }
                }

                // Update bodies
                foreach (IMassiveBody body in bodies)
                {
                    body.Update(timeStep);
                }
            }

            // Reset bodies to their starting rotations so spacecraft behave correctly
            foreach (IMassiveBody body in bodies)
            {
                body.ResetOrientation();
            }
        }

        /// <summary>
        /// Traces a massive body orbit by re-centering the world around the parent.
        /// </summary>
        public static void TraceMassiveBody(MassiveBodyBase body, OrbitTrace trace)
        {
            IMassiveBody parent = body.GravitationalParent;

            DVector2 initialPosition = body.Position - parent.Position;

            var proxyParent = new MassiveBodyProxy(DVector2.Zero, DVector2.Zero, parent);
            var proxySatellite = new MassiveBodyProxy(initialPosition, body.Velocity - parent.Velocity, body);

            double orbitalDt = GetOrbitalDt(initialPosition.Length(), parent.Mass, proxySatellite.Velocity.Length());

            trace.Reset(body.Position);

            // Assumes that the parent is (0,0) which will be true in this case
            double previousAngle = proxySatellite.Position.Angle();
            double totalAngularDisplacement = 0;

            // Max of 1000 steps, in practice it's 150-200
            for (int i = 0; i < 1000; i++)
            {
                proxySatellite.ResetAccelerations();

                proxySatellite.ResolveGravitation(proxyParent);

                proxySatellite.Update(orbitalDt);

                double currentAngle = proxySatellite.Position.Angle();

                totalAngularDisplacement += DeltaAngle(currentAngle, previousAngle);

                // Made a full orbit
                if (totalAngularDisplacement > AngularCutoff)
                {
                    break;
                }

                previousAngle = currentAngle;

                double altitude = proxyParent.GetRelativeHeight(proxySatellite.Position);

                trace.AddPoint(proxySatellite.Position + parent.Position, altitude);
            }
        }

        /// <summary>
        /// Traces a space craft orbit by re-centering the world around the parent.
        /// </summary>
        public static void TraceSpaceCraft(SpaceCraftBase satellite, OrbitTrace trace)
        {
            IMassiveBody parent = satellite.GravitationalParent;
            if (parent == null)
                return;

            DVector2 initialPosition = satellite.Position - parent.Position;

            var proxyParent = new MassiveBodyProxy(DVector2.Zero, DVector2.Zero, parent);
            var proxySatellite = new SpaceCraftProxy(initialPosition, satellite.Velocity - parent.Velocity, satellite);

            proxySatellite.SetGravitationalParent(proxyParent);

            int stepCount;

            double targetDt;
            double proximityDt;
            double orbitalDt = 0;

            bool isOrbiting;

            double altitude = proxyParent.GetRelativeHeight(proxySatellite.Position);
            double proximityAltitude = proxyParent.SurfaceRadius * 0.15;

            if (altitude < proximityAltitude)
            {
                stepCount = 1100;

                isOrbiting = false;

                proximityDt = GetProximityDt(altitude, proximityAltitude);

                targetDt = proximityDt;
            }
            else
            {
                stepCount = 600;

                isOrbiting = true;

                orbitalDt = GetOrbitalDt(initialPosition.Length(), parent.Mass, proxySatellite.Velocity.Length());

                targetDt = orbitalDt;
            }

            // Assumes that the parent is (0,0) which will be true in this case
            double previousAngle = proxySatellite.Position.Angle();
            double totalAngularDisplacement = 0;

            trace.Reset(satellite.Position);

            // Update steps based on orbit vs atmosphere
            for (int step = 0; step < stepCount; step++)
            {
                proxySatellite.ResetAccelerations();

                proxySatellite.ResolveGravitation(proxyParent);
                proxySatellite.ResolveAtmopsherics(proxyParent);

                proxySatellite.Update(targetDt);

                double currentAngle = proxySatellite.Position.Angle();

                totalAngularDisplacement += DeltaAngle(currentAngle, previousAngle);

                // Made a full orbit
                if (totalAngularDisplacement > AngularCutoff)
                {
                    break;
                }

                previousAngle = currentAngle;

                altitude = proxyParent.GetRelativeHeight(proxySatellite.Position);

                double velocity = proxySatellite.GetRelativeVelocity().Length();

                double offsetFactor = 0.0;

                //if (altitude < proxyParent.AtmosphereHeight*2)
                if (altitude < proxyParent.AtmosphereHeight * 4)
                {
                    if (velocity < 3000)
                    {
                        offsetFactor = 1.0;
                    }
                    else if (velocity < 4000)
                    {
                        offsetFactor = 1.0 - velocity / 3000.0;
                    }   
                }

                // Check if reference frame shifting needs to occur in atmosphere
                if (offsetFactor > 0.0001)
                {
                    DVector2 difference = proxyParent.Position - proxySatellite.Position;
                    difference.Normalize();

                    var surfaceNormal = new DVector2(-difference.Y, difference.X);

                    double altitudeFromCenter = altitude + proxyParent.SurfaceRadius;

                    // Distance of circumference at this altitude ( c= r * 2pi )
                    double pathCirumference = Constants.TwoPi * altitudeFromCenter;

                    double rotationalSpeed = pathCirumference / parent.RotationPeriod;

                    DVector2 atmopshereVelocity = surfaceNormal * rotationalSpeed;

                    proxySatellite.ApplyFrameOffset(atmopshereVelocity * offsetFactor * targetDt);
                }

                // Return early if the trace goes into a planet
                if (altitude <= 0)
                {
                    trace.AddPoint(proxySatellite.Position + parent.Position, altitude);
                    break;
                }

                // Determine the correct change overs from orbital to surface proximity
                if (isOrbiting)
                {
                    if (altitude < proximityAltitude)
                    {
                        proximityDt = GetProximityDt(altitude, proximityAltitude);

                        targetDt = MathHelper.Lerp(targetDt, proximityDt, 0.75);

                        isOrbiting = false;

                        stepCount = 1000;
                    }
                    else
                    {
                        targetDt = MathHelper.Lerp(targetDt, orbitalDt, 0.1);
                    }
                }
                else
                {
                    if (altitude > proximityAltitude)
                    {
                        orbitalDt = GetOrbitalDt(proxySatellite.Position.Length(), parent.Mass, proxySatellite.Velocity.Length());

                        targetDt = MathHelper.Lerp(targetDt, orbitalDt, 0.1);

                        isOrbiting = true;

                        stepCount = 600;
                    }
                    else
                    {
                        proximityDt = GetProximityDt(altitude, proximityAltitude);

                        targetDt = MathHelper.Lerp(targetDt, proximityDt, 0.75);
                    }
                }

                trace.AddPoint(proxySatellite.Position + parent.Position, altitude);
            }
        }

        // Finds the orbtial delta time step by assuming 300 points along the oribtal cirumference
        private static double GetOrbitalDt(double distance, double parentMass, double velocity)
        {
            double circularVelocity = Math.Sqrt((Constants.GravitationConstant * parentMass) / distance);

            double velocityRatio = velocity / circularVelocity;

            double approximateOrbitDiameter = distance * Constants.TwoPi;

            double approximateOrbitPeriod = approximateOrbitDiameter / velocity;

            return Math.Max(approximateOrbitPeriod * 0.0033 * velocityRatio, 125);
        }

        private static double GetProximityDt(double altitude, double proxityAltitude)
        {
            double altitudeRatio = altitude / proxityAltitude;

            return Math.Max(altitudeRatio * 15, 1);
        }

        private static double DeltaAngle(double angle1, double angle2)
        {
            double difference = angle2 - angle1;

            while (difference < -Constants.PiOverTwo)
            {
                difference += Math.PI;
            }

            while (difference > Constants.PiOverTwo)
            {
                difference -= Math.PI;
            }

            return Math.Abs(difference);
        }
    }
}


<|start_filename|>src/SpaceSim/Spacecrafts/ISpaceCraft.cs<|end_filename|>
﻿using System.Collections.Generic;
using SpaceSim.Controllers;
using SpaceSim.Drawing;
using SpaceSim.Engines;
using SpaceSim.Physics;
using VectorMath;

namespace SpaceSim.Spacecrafts
{
    interface ISpaceCraft : IGravitationalBody
    {
        string CraftName { get; }
        string CraftDirectory { get; }
        string CommandFileName { get; }

        ISpaceCraft Parent { get; }
        List<ISpaceCraft> Children { get; }

        double Width { get; }
        double Height { get; }

        double TotalWidth { get; }
        double TotalHeight { get; }

        double Throttle { get; }
        double Thrust { get; }
        double StagingForce { get; }

        double DryMass { get; }
        double PayloadMass { get; }
        double PropellantMass { get; }

        IEngine[] Engines { get; }
        IController Controller { get; }

        bool OnGround { get; }
        double OriginSurfaceAngle { get; }

        void SkewEventTimes(double delay);
        void Initialize(SpaceCraftManager spaceCraftManager, EventManager eventManager, double clockDelay);

        void Stage();
        void Release();
        void DeployBattery();
        void DeployFairing();
        void DeployLAS();

        void ToggleDisplayVectors();

        void SetSurfacePosition(DVector2 positon, double surfaceAngle);
        double GetDownrangeDistance(DVector2 pointOfReference);

        void SetDihedral(double dihedral, int[] finIds = null);
        double GetDihedral(int finId);
        void SetThrottle(double throttle, int[] engineIds = null);

        void SetPitch(double pitch);
        void OffsetPitch(double offset);

        double Roll { get; }
        void OffsetRoll(double offset);
        void SetRoll(double roll);

        void OffsetYaw(double offset);
        void SetYaw(double yaw);

        void OffsetRelativePitch(double offset);
        void SetRelativePitch(double pitch);

        void SetParent(ISpaceCraft craft);
        void AddChild(ISpaceCraft child);
        void RemoveChild(ISpaceCraft child);

        void UpdateController(double dt);
        void UpdateAnimations(TimeStep timeStep);
        void UpdateChildren(DVector2 position, DVector2 velocity);

        bool Terminated { get; }
        void Terminate();
    }
}


<|start_filename|>src/SpaceSim/SolarSystem/Planets/Earth.cs<|end_filename|>
﻿using System;
using System.Drawing;
using SpaceSim.Kernels;
using SpaceSim.Orbits;

using SpaceSim.Properties;
using VectorMath;

namespace SpaceSim.SolarSystem.Planets
{
    class Earth : MassiveBodyBase
    {
        public override string ApoapsisName { get { return "Apogee"; } }
        public override string PeriapsisName { get { return "Perigee"; } }

        private double inclination = 0.0;

        public override double Mass
        {
            get { return 5.97219e24; }
        }

        public override double SurfaceRadius
        {
            get { return BaseKernel.EARTH_RADIUS; }
        }

        public override double AtmosphereHeight
        {
            get { return BaseKernel.EARTH_ATMOSPHERE; }
        }

        public override double RotationRate
        {
            get { return -7.2722052166e-5 * inclination; }
        }

        public override double RotationPeriod
        {
            get { return 86400 / inclination; }
        }

        public override Color IconColor { get { return Color.Green; } }
        public override Color IconAtmopshereColor { get { return Color.LightSkyBlue; } }

        public Earth(double inclination)
            : base(OrbitHelper.FromJplEphemeris(1.470669705624798E+08, -3.465263667484938E+07),
                   OrbitHelper.FromJplEphemeris(6.445708901348731E+00, 2.887242866160539E+01), new EarthKernel())
        {
            this.inclination = Math.Cos(inclination * MathHelper.DegreesToRadians); ;
        }

        // Realistic density model based off https://www.grc.nasa.gov/www/k-12/rocket/atmos.html
        public override double GetAtmosphericDensity(double altitude)
        {
            if (altitude > AtmosphereHeight) return 0;

            double temperature;
            double pressure;

            if (altitude > 25000)
            {
                temperature = -131.21 + 0.00299 * altitude;

                pressure = 2.448 * Math.Pow((temperature + 273.1) / 216.6, -11.388);
            }
            else if (altitude > 11000)
            {
                temperature = -56.46;

                pressure = 22.65 * Math.Exp(1.73 - 0.000157 * altitude);
            }
            else
            {
                temperature = 15.04 - 0.00649 * altitude;

                pressure = 101.29 * Math.Pow((temperature + 273.1) / 288.08, 5.256);   
            }

            return pressure / (0.2869 * (temperature + 273.1));
        }

        // Quickly approximated using temperature from here https://www.grc.nasa.gov/www/k-12/rocket/atmos.html
        // and a calculator here http://www.mhtl.uwaterloo.ca/old/onlinetools/airprop/airprop.html
        public override double GetAtmosphericViscosity(double altitude)
        {
            if (altitude > AtmosphereHeight) return 0;

            if (altitude > 10668) return 0.0000089213;

            return -5.37e-10 * altitude + 1.458e-5;
        }

        public override string ToString()
        {
            return "Earth";
        }
    }
}


<|start_filename|>src/SpaceSim/Spacecrafts/Electron/ElectronKickStage.cs<|end_filename|>
﻿using System;
using SpaceSim.Engines;
using SpaceSim.Physics;
using VectorMath;
using SpaceSim.Common;
using System.Drawing;
using SpaceSim.Drawing;

using SpaceSim.Properties;
using System.IO;

namespace SpaceSim.Spacecrafts.Electron
{
    class ElectronKickStage : SpaceCraftBase
    {
        public override string CraftName { get { return "Electron Kick Stage"; } }
        public override string CommandFileName { get { return "ElectronKickStage.xml"; } }

        public override double DryMass {
            get
            {
                if (!_deployedFairings)
                {
                    return 25 + _leftFairing.DryMass + _rightFairing.DryMass;
                }

                return 25;
            }
        }

        public override double Width { get { return 1.2; } }
        public override double Height { get { return 0.8; } }

        public override AeroDynamicProperties GetAeroDynamicProperties { get { return AeroDynamicProperties.ExtendsFineness; } }

        public override double LiftCoefficient
        {
            get
            {
                if (!_deployedFairings)
                {
                    return _leftFairing.LiftCoefficient + _rightFairing.LiftCoefficient;
                }

                return 1;
            }
        }

        public override double FrontalArea
        {
            get
            {
                if (!_deployedFairings)
                {
                    return _leftFairing.FrontalArea + _rightFairing.FrontalArea;
                }

                return 1;
            }
        }

        public override double FormDragCoefficient
        {
            get
            {
                if (!_deployedFairings)
                {
                    return _leftFairing.FormDragCoefficient + _rightFairing.FormDragCoefficient;
                }

                return 1;
            }
        }

        public override double StagingForce { get { return 10; } }

        public override double ExposedSurfaceArea
        {
            get
            {
                // A = 2πrh + πr2
                return 2 * Math.PI * (Width / 2) * Height + FrontalArea;
            }
        }

        public override double LiftingSurfaceArea
        {
            get
            {
                return Width * Height;
            }
        }

        public override Color IconColor
        {
            get
            {
                return Color.White;
            }
        }

        private bool _deployedBatteries = false;
        private bool _deployedFairings = false;
        private ElectronBattery _leftBattery;
        private ElectronBattery _rightBattery;
        private ElectronFairing _leftFairing;
        private ElectronFairing _rightFairing;
        DateTime timestamp = DateTime.Now;

        public ElectronKickStage(string craftDirectory, DVector2 position, DVector2 velocity, double payloadMass = 50, double propellantMass = 100)
            : base(craftDirectory, position, velocity, payloadMass, propellantMass, "Electron/KickStage.png")
        {
            StageOffset = new DVector2(0, 0);

            Engines = new IEngine[]
            {
                new Curie(this, new DVector2(0, Height * 0.3)), 
            };
        }

        public void AttachBatteries(ElectronBattery leftBattery, ElectronBattery rightBattery)
        {
            _leftBattery = leftBattery;
            _rightBattery = rightBattery;

            _leftBattery.SetParent(this);
            _rightBattery.SetParent(this);
        }

        public void AttachFairings(ElectronFairing leftFairing, ElectronFairing rightFairing)
        {
            _leftFairing = leftFairing;
            _rightFairing = rightFairing;

            _leftFairing.SetParent(this);
            _rightFairing.SetParent(this);
        }

        public override void Release()
        {
            _rightFairing.Release();
            _leftFairing.Release();

            base.Release();
        }

        public override void Update(double dt)
        {
            base.Update(dt);

            if (!_deployedBatteries)
            {
                _leftBattery.UpdateChildren(Position, Velocity);
                _rightBattery.UpdateChildren(Position, Velocity);

                _leftBattery.SetPitch(Pitch);
                _rightBattery.SetPitch(Pitch);
            }

            if (!_deployedFairings)
            {
                _leftFairing.UpdateChildren(Position, Velocity);
                _rightFairing.UpdateChildren(Position, Velocity);

                _leftFairing.SetPitch(Pitch);
                _rightFairing.SetPitch(Pitch);
            }
        }

        public override void DeployBattery()
        {
            _deployedBatteries = true;

            _leftBattery.Stage();
            _rightBattery.Stage();
        }

        public override void DeployFairing()
        {
            _deployedFairings = true;

            _leftFairing.Stage();
            _rightFairing.Stage();
        }

        public override void RenderGdi(Graphics graphics, Camera camera)
        {
            base.RenderGdi(graphics, camera);

            _leftFairing.RenderGdi(graphics, camera);
            _rightFairing.RenderGdi(graphics, camera);

            if (Settings.Default.WriteCsv && (DateTime.Now - timestamp > TimeSpan.FromSeconds(1)))
            {
                string filename = MissionName + ".csv";

                if (!File.Exists(filename))
                {
                    File.AppendAllText(filename, "Velocity, Acceleration, Altitude, Throttle\r\n");
                }

                timestamp = DateTime.Now;

                string contents = string.Format("{0}, {1}, {2}, {3}\r\n",
                    this.GetRelativeVelocity().Length(),
                    this.GetRelativeAcceleration().Length() * 100,
                    this.GetRelativeAltitude() / 100,
                    //this.GetRelativeAltitude() / 1000,
                    this.Throttle * 10);
                File.AppendAllText(filename, contents);
            }
        }
    }
}


<|start_filename|>src/SpaceSim/Commands/DeployCommand.cs<|end_filename|>
﻿using System;
using SpaceSim.Common.Contracts.Commands;
using SpaceSim.Spacecrafts;

namespace SpaceSim.Commands
{
    class DeployCommand : CommandBase
    {
        private string _part;

        public DeployCommand(Deploy deloy)
            : base(deloy.StartTime, deloy.Duration)
        {
            if (string.IsNullOrEmpty(deloy.Part))
            {
                throw new Exception("Deployment part must be specified in the flight profile!");
            }

            _part = deloy.Part;
        }

        public override void Initialize(SpaceCraftBase spaceCraft)
        {
            EventManager.AddMessage(string.Format("Deploying " + _part), spaceCraft);

            switch (_part)
            {
                case "Battery":
                    spaceCraft.DeployBattery();
                    break;
                case "Fairing":
                    spaceCraft.DeployFairing();
                    break;
                case "Grid Fins":
                    spaceCraft.DeployGridFins();
                    break;
                case "Landing Legs":
                    spaceCraft.DeployLandingLegs();
                    break;
                case "Drogues":
                    spaceCraft.DeployDrogues();
                    break;
                case "Parachutes":
                    spaceCraft.DeployParachutes();
                    break;
                case "LAS":
                    spaceCraft.DeployLAS();
                    break;
                default:
                    throw new Exception(string.Format("{0} is not a known deployable!", _part));
            }
        }

        public override void Finalize(SpaceCraftBase spaceCraft) { }

        public override void Update(double elapsedTime, SpaceCraftBase spaceCraft) { }
    }
}


<|start_filename|>src/SpaceSim/Spacecrafts/SLS/Interstage.cs<|end_filename|>
﻿using System;
using System.Drawing;
using SpaceSim.Engines;
using SpaceSim.Physics;
using VectorMath;

namespace SpaceSim.Spacecrafts.DragonV2
{
    class Interstage : SpaceCraftBase
    {
        public override string CraftName { get { return "Interstage"; } }
        public override string CommandFileName { get { return "Interstage.xml"; } }

        public override double Width { get { return 4.5; } }
        public override double Height { get { return 3.0; } }
        //public override double Height { get { return 5.0; } }

        public override double DryMass { get { return 0; } }

        public override AeroDynamicProperties GetAeroDynamicProperties { get { return AeroDynamicProperties.ExtendsFineness; } }

        public override double FormDragCoefficient
        {
            get
            {
                double baseCd = GetBaseCd(0.3);
                double alpha = GetAlpha();
                double cosAlpha = Math.Cos(alpha);
                double Cd = Math.Abs(baseCd * cosAlpha);

                return Cd;
            }
        }

        public override double LiftCoefficient
        {
            get
            {
                double baseCd = GetBaseCd(0.6);
                double alpha = GetAlpha();
                double sinAlpha = Math.Sin(alpha * 2);
                return baseCd * sinAlpha;
            }
        }

        // Cylinder - 2 * pi * r * h
        public override double FrontalArea { get { return 27.6579; } }

        public override double ExposedSurfaceArea
        {
            get
            {
                // A = 2πrh + πr2
                return 2 * Math.PI * (Width / 2) * Height + FrontalArea;
            }
        }

        public override double LiftingSurfaceArea { get { return Width * Height; } }

        public override Color IconColor { get { return Color.White; } }

        public Interstage(string craftDirectory, DVector2 position, DVector2 velocity, double yOffset = 3.0)
            : base(craftDirectory, position, velocity, 0, 0, "SLS/Interstage.png")
        {
            StageOffset = new DVector2(0, yOffset);

            Engines = new IEngine[]{};
        }
    }
}


<|start_filename|>src/SpaceSim/Structures/CrewArm.cs<|end_filename|>
﻿using System.Drawing;
using SpaceSim.SolarSystem;

namespace SpaceSim.Structures
{
    class CrewArm : StructureBase
    {
        public override double Width { get { return 16; } }
        public override double Height { get { return 9; } }

        public override Color IconColor { get { return Color.White; } }

        public CrewArm(double surfaceAngle, double height, IMassiveBody parent)
            : base(surfaceAngle, height, "Textures/Structures/crewArm.png", parent)
        {
        }
    }
}


<|start_filename|>src/SpaceSim/Spacecrafts/StarlinkSat.cs<|end_filename|>
﻿using System;
using System.Drawing;
using System.IO;
using SpaceSim.Drawing;
using SpaceSim.Engines;
using SpaceSim.Physics;
using SpaceSim.Spacecrafts.FalconCommon;
using VectorMath;

using SpaceSim.Properties;

namespace SpaceSim.Spacecrafts
{
    class StarlinkSat : SpaceCraftBase
    {
        public override string CraftName { get { return _craftName; } }
        public override string CommandFileName { get { return "StarlinkSat.xml"; } }

        public override double Width { get { return 1; } }
        public override double Height { get { return 1; } }

        public override double DryMass
        {
            get
            {
                return 125;
            }
        }

        public override AeroDynamicProperties GetAeroDynamicProperties { get { return AeroDynamicProperties.None; } }

        public override double FormDragCoefficient
        {
            get
            {
                return 1;
            }
        }

        public override double LiftCoefficient
        {
            get
            {
                return 1;
            }
        }

        public override double FrontalArea
        {
            get
            {
                return 1;
            }
        }

        public override double ExposedSurfaceArea
        {
            get
            {
                return 1;
            }
        }

        public override double LiftingSurfaceArea
        {
            get
            {
                return 1;
            }
        }

        public override Color IconColor { get { return Color.White; } }

        private string _craftName;

        DateTime timestamp = DateTime.Now;

        public StarlinkSat(string craftDirectory, DVector2 position, DVector2 velocity, double payloadMass, double propellantMass = 125)
            : base(craftDirectory, position, velocity, payloadMass, propellantMass, "Satellites/default.png")
        {
            //_craftName = new DirectoryInfo(craftDirectory).Name;
            _craftName = "StarlinkSat";

            Engines = new IEngine[]
            {
                new StarDrive(0, this, new DVector2(0, Height * 0.33))
            };
        }

        public override void RenderGdi(Graphics graphics, Camera camera)
        {
            base.RenderGdi(graphics, camera);

            if (Settings.Default.WriteCsv && (DateTime.Now - timestamp > TimeSpan.FromSeconds(1)))
            {
                string filename = MissionName + ".csv";

                if (!File.Exists(filename))
                {
                    File.AppendAllText(filename, "Velocity, Acceleration, Altitude, Throttle\r\n");
                }

                timestamp = DateTime.Now;

                string contents = string.Format("{0}, {1}, {2}, {3}\r\n",
                    this.GetRelativeVelocity().Length(),
                    this.GetRelativeAcceleration().Length() * 1000,
                    //this.GetRelativeAltitude() / 100,
                    this.GetRelativeAltitude() / 100,
                    this.Throttle * 100);
                File.AppendAllText(filename, contents);
            }
        }
    }
}



<|start_filename|>src/SpaceSim/Gauges/NavBall.cs<|end_filename|>
﻿using System;
using System.Drawing;
using VectorMath;

namespace SpaceSim.Gauges
{
    public class NavBall : IGauge
    {
        public RectangleF Bounds { get; private set; }

        private Point _center;
        private double _thrustAngle;
        private double _flightPathAngle;

        public NavBall(Point center)
        {
            _center = center;

            Bounds = new RectangleF(_center.X - 55, _center.Y - 55, 110, 110);
        }

        public void Update(double thrustAngle, double thrustMagnitude, double flightPathAngle)
        {
            _thrustAngle = thrustAngle;
            _flightPathAngle = flightPathAngle;
        }

        public void Render(Graphics graphics, RectangleD cameraBounds)
        {
            var end = new PointF(_center.X + (float)Math.Cos(_thrustAngle) * 50, _center.Y + (float)Math.Sin(_thrustAngle) * 50);
            graphics.DrawLine(new Pen(Color.Red, 2), _center, end);

            end = new PointF(_center.X + (float)Math.Cos(_flightPathAngle) * 50, _center.Y + (float)Math.Sin(_flightPathAngle) * 50);
            graphics.DrawLine(new Pen(Color.Yellow, 2), _center, end);

            graphics.DrawEllipse(new Pen(Color.White, 2), Bounds);
        }
    }
}


<|start_filename|>src/SpaceSim/Engines/Merlin1DB5.cs<|end_filename|>
﻿using System.Drawing;
using SpaceSim.Particles;
using SpaceSim.Spacecrafts;
using VectorMath;

namespace SpaceSim.Engines
{
    class Merlin1DB5 : EngineBase
    {
        public Merlin1DB5(int id, ISpaceCraft parent, DVector2 offset)
            : base(parent, offset, new EngineFlame(id, Color.FromArgb(63, 255, 255, 191), 30, 2, 0.2, 0.8, 0.1))
        {
        }

        public override double Thrust(double ispMultiplier)
        {
            return (854000 + 70000 * ispMultiplier) * Throttle * 0.01;
        }

        // Based off of ISP = F/m*g0
        public override double MassFlowRate(double ispMultiplier)
        {
            return 308.8 * Throttle * 0.01;
        }

        public override IEngine Clone()
        {
            return new Merlin1D(0, Parent, Offset);
        }

        public override string ToString()
        {
            return "Merlin1D";
        }
    }
}


<|start_filename|>src/SpaceSim/Spacecrafts/Fin.cs<|end_filename|>
﻿using System;
using SpaceSim.Common;
using VectorMath;

namespace SpaceSim.Spacecrafts
{
    class Fin : SpaceCraftPart, IFin
    {
        public override double Width { get; protected set; }
        public override double Height { get; protected set; }
        protected override double DrawingOffset { get; }

        public double Dihedral { get; protected set; }

        private double _offsetLength;
        private double _offsetRotation;
        private double _offsetRatio;

        private double _width;
        private double _height;

        public Fin(ISpaceCraft parent, DVector2 offset, DVector2 size, double dihedral = 0.0, string texturePath = "Textures/Spacecrafts/ITS/Fin.png", double offsetRatio = 1.5)
            : base(parent, texturePath)
        {
            _width = size.X;
            _height = size.Y;

            Width = _width;
            Height = _height;

            Dihedral = dihedral;

            DrawingOffset = 0.0;

            _offsetLength = offset.Length();
            _offsetRotation = offset.Angle() - Constants.PiOverTwo;
            _offsetRatio = offsetRatio;
        }

        public void SetDihedral(double targetAngle)
        {
            Dihedral = targetAngle;
        }

        public override void Update(double dt)
        {
            double rotation = _parent.Pitch - _offsetRotation;
            DVector2 offset = new DVector2(Math.Cos(rotation), Math.Sin(rotation)) * _offsetLength;

            // handle dihedral
            offset.X += Math.Sin(Dihedral) * Math.Cos(rotation - Constants.PiOverTwo) * _width / _offsetRatio;
            offset.Y += Math.Sin(Dihedral) * Math.Sin(rotation - Constants.PiOverTwo) * _width / _offsetRatio;

            Position = _parent.Position - offset;

            Width = -Math.Sin(Dihedral) * _width;
        }
    }
}


<|start_filename|>src/SpaceSim/Spacecrafts/Falcon9/F9S2B5.cs<|end_filename|>
﻿using System;
using System.Drawing;
using SpaceSim.Engines;
using SpaceSim.Physics;
using VectorMath;

namespace SpaceSim.Spacecrafts.Falcon9
{
    sealed class F9S2B5 : SpaceCraftBase
    {
        public override string CraftName { get { return "F9 S2"; } }
        public override string CommandFileName { get { return "F9S2B5.xml"; } }

        public override double DryMass { get { return 4500; } }

        public override double Width { get { return 3.706; } }
        public override double Height { get { return 14.0018; } }

        public override AeroDynamicProperties GetAeroDynamicProperties { get { return AeroDynamicProperties.ExtendsFineness; } }

        public override double FormDragCoefficient
        {
            get
            {
                double baseCd = GetBaseCd(0.5);
                double alpha = GetAlpha();

                return Math.Abs(baseCd * Math.Cos(alpha));
            }
        }

        public override double LiftCoefficient
        {
            get
            {
                double baseCd = GetBaseCd(0.6);
                double alpha = GetAlpha();

                return baseCd * Math.Sin(alpha * 2);
            }
        }

        public override double FrontalArea { get { return Math.PI * Math.Pow(Width / 2, 2); } }

        public override double ExposedSurfaceArea
        {
            get
            {
                // A = 2πrh + πr2
                return 2 * Math.PI * (Width / 2.0) * Height + FrontalArea;
            }
        }

        public override double LiftingSurfaceArea { get { return Width * Height; } }

        public override Color IconColor { get { return Color.White; } }

        public F9S2B5(string craftDirectory, DVector2 position, DVector2 velocity, double stageOffset, double propellantMass = 105750)
            : base(craftDirectory, position, velocity, 0, propellantMass, "Falcon/9/S2.png")
        {
            StageOffset = new DVector2(0, stageOffset);

            Engines = new IEngine[]
            {
                new Merlin1DB5Vac(this, new DVector2(0, Height * 0.38))
            };
        }
    }
}



<|start_filename|>src/SpaceSim/Kernels/EarthKernel.cs<|end_filename|>
﻿using SpaceSim.SolarSystem;

namespace SpaceSim.Kernels
{
    class EarthKernel : BaseKernel, IMassiveKernel
    {
        // -------- ------ Image ----- --------
        // 01111111 00000000 00000000 00000000 alpha >> 24
        // 00000000 11111111 00000000 00000000 red >> 16
        // 00000000 00000000 11111111 00000000 green >> 8
        // 00000000 00000000 00000000 11111111 blue
        public void Run(int[] image, int resX, int resY, double cX, double cY, double cWidth, double cHeight, double cRot, double sunNormalX, double sunNormalY, double bodyX, double bodyY, double bodyRot)
        {
            int index = get_global_id(0);

            double diffX, diffY;
            ComputeCameraRotation(index, resX, resY, cX, cY, cWidth, cHeight, cRot, bodyX, bodyY, out diffX, out diffY);

            double distance = sqrt(diffX * diffX + diffY * diffY);

            if (distance < EARTH_RADIUS + EARTH_ATMOSPHERE)
            {
                double worldNormalX, worldNormalY, sunDotProduct;
                ComputeSunShading(diffX, diffY, distance, sunNormalX, sunNormalY, out worldNormalX, out worldNormalY, out sunDotProduct);

                if (distance < EARTH_RADIUS)
                {
                    double ratio = distance / EARTH_RADIUS;

                    double worldAngle = atan2(worldNormalX, worldNormalY) + atan2(ratio, 1.1) + bodyRot;

                    double textureFactor = 0.25 * sin(worldAngle * 500) + 0.75;

                    int red = (int)(sunDotProduct * 95 * ratio * textureFactor);
                    int green = (int)(sunDotProduct * 169 * ratio * textureFactor);
                    int blue = (int)(sunDotProduct * 47 * ratio * textureFactor);

                    image[index] = ALPHA | (green << 8) | (red << 16);
                }
                else
                {
                    double ratio = (distance - EARTH_RADIUS) / EARTH_ATMOSPHERE;
                    
                    int red = (int)((95 - ratio * 95) * sunDotProduct);
                    int green = (int)((188 - ratio * 188) * sunDotProduct);
                    int blue = (int)((255 - ratio * 255) * sunDotProduct);

                    image[index] = ALPHA | blue | (green << 8) | (red << 16);
                }
            }
        }
    }
}


<|start_filename|>src/SpaceSim/Spacecrafts/SLS/LAS.cs<|end_filename|>
﻿using System;
using System.Drawing;
using SpaceSim.Drawing;
using SpaceSim.Engines;
using SpaceSim.Physics;
using VectorMath;

namespace SpaceSim.Spacecrafts.FalconCommon
{
    class LAS : SpaceCraftBase
    {
        public override string CraftName { get { return "LAS"; } }
        public override string CommandFileName { get { return "LAS.xml"; } }

        public override double Width { get { return 5.2; } }
        public override double Height { get { return 11.6 + 3.3 + 2.46; } }
        public override double DryMass { get { return 3696; } }

        public override AeroDynamicProperties GetAeroDynamicProperties
        {
            get { return AeroDynamicProperties.ExposedToAirFlow; }
        }

        public override double StagingForce { get { return 1500; } }

        public override double FormDragCoefficient
        {
            get
            {
                double baseCd = GetBaseCd(0.4);
                double alpha = GetAlpha();
                return baseCd * Math.Cos(alpha);
            }
        }

        public override double LiftCoefficient
        {
            get
            {
                double baseCd = GetBaseCd(0.6);
                double alpha = GetAlpha();
                return baseCd * Math.Sin(alpha * 2.0);
            }
        }

        public override double FrontalArea { get { return Math.PI * Math.Pow(Width / 2, 2); } }
        public override double ExposedSurfaceArea { get { return 2 * Math.PI * (Width / 2) * Height + FrontalArea; } }
        public override double LiftingSurfaceArea { get { return Width * Height; } }
        public override Color IconColor { get { return Color.White; } }

        public LAS(string craftDirectory, DVector2 position, DVector2 velocity, double propellantMass = 2480)
            : base(craftDirectory, position, velocity, 0, propellantMass, "SLS/LAS.png", null)
        {
            StageOffset = new DVector2(0, -3.2);

            Engines = new IEngine[]
            {
                new OrionAbortMotor(0, this, new DVector2(-0.2, -3.5), -0.5),
                new OrionAbortMotor(1, this, new DVector2(-0.05, -3.5), -0.1),
                new OrionAbortMotor(2, this, new DVector2(0.05, -3.5), 0.1),
                new OrionAbortMotor(3, this, new DVector2(0.2, -3.5), 0.5)
            };
        }
    }
}

<|start_filename|>src/SpaceSim/Structures/TowersRight.cs<|end_filename|>
﻿using System.Drawing;
using SpaceSim.SolarSystem;

namespace SpaceSim.Structures
{
    class TowersRight : StructureBase
    {
        public override double Width { get { return 20; } }
        public override double Height { get { return 100; } }

        public override Color IconColor { get { return Color.White; } }

        public TowersRight(double surfaceAngle, double height, IMassiveBody parent)
            : base(surfaceAngle, height, "Textures/Structures/towersRight.png", parent)
        {
        }
    }
}


<|start_filename|>src/SpaceSim/Engines/SuperDraco.cs<|end_filename|>
﻿using System.Drawing;
using SpaceSim.Particles;
using SpaceSim.Spacecrafts;
using VectorMath;

namespace SpaceSim.Engines
{
    class SuperDraco : EngineBase
    {
        private double _angle;

        public SuperDraco(int id, ISpaceCraft parent, DVector2 offset, double angle)
            : base(parent, offset, new EngineFlame(id, Color.FromArgb(63, 255, 255, 159), 50, 2, 0.1, 0.15, 0.003, angle))
        {
            _angle = angle;
        }

        public override double Thrust(double ispMultiplier)
        {
            return 71000 * Throttle * 0.01;
        }

        public override double MassFlowRate(double ispMultiplier)
        {
            return 30.8 * Throttle * 0.01;
        }

        public override IEngine Clone()
        {
            return new SuperDraco(0, Parent, Offset, _angle);
        }

        public override string ToString()
        {
            return "Super Draco";
        }
    }
}


<|start_filename|>src/SpaceSim/SolarSystem/Moons/Io.cs<|end_filename|>
﻿using System.Drawing;
using SpaceSim.Kernels;
using SpaceSim.Orbits;
using VectorMath;

namespace SpaceSim.SolarSystem.Moons
{
    class Io : MassiveBodyBase
    {
        public override double Mass
        {
            get { return 8.931938e22; }
        }

        public override double SurfaceRadius
        {
            get { return BaseKernel.IO_RADIUS; }
        }

        public override double AtmosphereHeight
        {
            get { return 0.0; }
        }

        public override double RotationRate
        {
            get { return -4.1105923995997146089500714672169e-5; }
        }

        public override double RotationPeriod
        {
            get { return 152853.5232; }
        }

        public override Color IconColor { get { return Color.DarkKhaki; } }
        public override Color IconAtmopshereColor { get { return Color.White; } }

        public Io(DVector2 parentPositon, DVector2 parentVelocity)
            : base(OrbitHelper.FromJplEphemeris(-4.187887882620349E+05, -3.532782596227979E+04) + parentPositon,
                OrbitHelper.FromJplEphemeris(1.513295262903566E+00, -1.731988564333744E+01) + parentVelocity, new IoKernel())
        {
        }

        public override double GetAtmosphericDensity(double height)
        {
            return 0.0;
        }

        public override string ToString()
        {
            return "Io";
        }
    }
}


<|start_filename|>src/SpaceSim/Engines/RL10.cs<|end_filename|>
﻿using System.Drawing;
using SpaceSim.Particles;
using SpaceSim.Spacecrafts;
using VectorMath;

namespace SpaceSim.Engines
{
    class RL10 : EngineBase
    {
        public RL10(int id, ISpaceCraft parent, DVector2 offset)
            : base(parent, offset, new EngineFlame(id, Color.FromArgb(63, 221, 192, 220), 100, 2, 1.0, 1.2, 0.1))
        {
        }

        public override double Thrust(double ispMultiplier)
        {
            return (100000.0 + 10000.0 * ispMultiplier) * Throttle * 0.01;
        }

        // Based off of Isp = F/ṁ*g0 => ṁ = F/Isp*g0 Isp = 465.5
        public override double MassFlowRate(double ispMultiplier)
        {
            return 24.14 * Throttle * 0.01;
        }

        public override IEngine Clone()
        {
            return new RL10(0, Parent, Offset);
        }

        public override string ToString()
        {
            return "RL-10";
        }
    }
}
