public static int Prev(int currentIndex, IList <IGravitationalBody> bodies) { HashSet <int> connectedBodies = GetConnectedBodies(currentIndex, bodies); List <Tuple <double, int> > bodiesByDistance = GetSortedBodyDistances(currentIndex, bodies); foreach (Tuple <double, int> bodyDistancePair in bodiesByDistance) { int targetId = bodyDistancePair.Item2; // The closest object is still part of the same craft if (connectedBodies.Contains(targetId)) { continue; } IGravitationalBody body = bodies[targetId]; if (body.Position.X < bodies[currentIndex].Position.X) { return(GetParentIndex(targetId, bodies)); } } return(currentIndex); }
private void SetCameraRotation() { IGravitationalBody target = _gravitationalBodies[_targetIndex]; if (target is ISpaceCraft) { if (_rotateInOrbit) { if (target.InOrbit) { if (!_targetInOrbit) { _camera.SetRotation(0, true); _targetInOrbit = true; } else { _camera.SetRotation(0); } } else { DVector2 craftOffset = target.GravitationalParent.Position - target.Position; craftOffset.Normalize(); if (_targetInOrbit) { _camera.SetRotation(Constants.PiOverTwo - craftOffset.Angle(), true); _targetInOrbit = false; } else { _camera.SetRotation(Constants.PiOverTwo - craftOffset.Angle()); } } } else { DVector2 craftOffset = target.GravitationalParent.Position - target.Position; craftOffset.Normalize(); _camera.SetRotation(Constants.PiOverTwo - craftOffset.Angle()); } } else { _camera.SetRotation(0); } }
private static List <int> ConnectedBodyHelper(IGravitationalBody target, IList <IGravitationalBody> bodies) { var connections = new List <int>(); // Find all bodies connected to the target for (int i = 0; i < bodies.Count; i++) { var craft = bodies[i] as ISpaceCraft; if (craft != null && craft.Parent != null) { if (craft.Parent == target) { connections.Add(i); // Recursively add all the bodies connected to this one connections.AddRange(ConnectedBodyHelper(craft, bodies)); } } } return(connections); }
/// <summary> /// Draws all the physics bodies and UI elements. /// </summary> private unsafe void DrawFrame(TimeStep timeStep, FpsManager frameTimer) { _textDisplay.Clear(); RectangleD cameraBounds = _camera.Bounds; IGravitationalBody target = _gravitationalBodies[_targetIndex]; var targetSpaceCraft = target as SpaceCraftBase; // If openCL is supported render all cl bodies if (_renderingType == RenderingType.OpenCLHardware || _renderingType == RenderingType.OpenCLSoftware) { _gpuClear.RenderCl(_clProxy); foreach (MassiveBodyBase renderable in _massiveBodies) { if (renderable.Visibility(cameraBounds) > 0) { renderable.RenderCl(_clProxy, cameraBounds, _sun); } } int[] frameData = _clProxy.ReadIntBuffer("image", RenderUtils.ScreenArea); var rect = new Rectangle(0, 0, _imageBitmap.Width, _imageBitmap.Height); BitmapData bmpData = _imageBitmap.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); Marshal.Copy(frameData, 0, bmpData.Scan0, RenderUtils.ScreenArea); var ptr = (byte *)bmpData.Scan0; // Hack to force full alpha for now for (int i = 0; i < RenderUtils.ScreenArea; i++) { ptr[i * 4 + 3] = 255; } _imageBitmap.UnlockBits(bmpData); } else { // Fall back to gdi for cl renderables using (var graphics = Graphics.FromImage(_imageBitmap)) { graphics.Clear(Color.Black); _camera.ApplyScreenRotation(graphics); foreach (MassiveBodyBase renderable in _massiveBodies) { if (renderable.Visibility(cameraBounds) > 0) { renderable.RenderGdiFallback(graphics, cameraBounds, _sun); } } } } // Draw all orbit traces, spacecrafts, and GDI objects using (Graphics graphics = RenderUtils.GetContext(false, _imageBitmap)) { _camera.ApplyScreenRotation(graphics); //RenderUtils.DrawLine(graphics, cameraBounds, new DVector2(0, -10e12), new DVector2(0, 10e12), Color.FromArgb(40, 255, 255, 255)); //RenderUtils.DrawLine(graphics, cameraBounds, new DVector2(-10e12, 0), new DVector2(10e12, 0), Color.FromArgb(40, 255, 255, 255)); // Draw all massive body orbit traces foreach (MassiveBodyBase massiveBody in _massiveBodies) { if (massiveBody is Sun) { continue; } massiveBody.RenderGdi(graphics, _camera); } graphics.ResetTransform(); // Draw structures foreach (StructureBase structure in _structures) { structure.RenderGdi(graphics, _camera); } // Draw spacecraft foreach (SpaceCraftBase spaceCraft in _spaceCrafts) { spaceCraft.RenderGdi(graphics, _camera); } } // Draw all GUI elements (higher quality) using (Graphics graphics = RenderUtils.GetContext(true, _imageBitmap)) { double throttle = 0; if (targetSpaceCraft != null) { throttle = targetSpaceCraft.Throttle; } foreach (IGauge gauge in _gauges) { if (targetSpaceCraft != null) { gauge.Update(_gravitationalBodies[_targetIndex].GetRelativePitch(), throttle / 100.0); } gauge.Render(graphics, cameraBounds); } _eventManager.Render(graphics); var elapsedTime = TimeSpan.FromSeconds(_totalElapsedSeconds - ClockDelayInSeconds); int elapsedYears = elapsedTime.Days / 365; int elapsedDays = elapsedTime.Days % 365; DateTime localTime = _originTime + elapsedTime; // Main timing display _textDisplay.AddTextBlock(StringAlignment.Near, new List <string> { $"Origin Time: {localTime.ToShortDateString()} {localTime.ToShortTimeString()}", $"Elapsed Time: Y:{elapsedYears} D:{elapsedDays} H:{elapsedTime.Hours} M:{elapsedTime.Minutes} S:{elapsedTime.Seconds}", $"Update Speed: {timeStep.Multiplier}" }); // Target display _textDisplay.AddTextBlock(StringAlignment.Center, string.Format("Target: {0}", target)); // FPS _textDisplay.AddTextBlock(StringAlignment.Far, "FPS: " + frameTimer.CurrentFps); double targetVelocity = target.GetRelativeVelocity().Length(); // Info for altitude var altitudeInfo = new List <string> { "Altitude: " + UnitDisplay.Distance(target.GetRelativeAltitude()) }; // Add downrange if spacecraft exists if (targetSpaceCraft != null) { double downrangeDistance = targetSpaceCraft.GetDownrangeDistance(_structures[0].Position); altitudeInfo.Add("Downrange: " + UnitDisplay.Distance(downrangeDistance)); } _textDisplay.AddTextBlock(StringAlignment.Near, altitudeInfo); // Info for speed / acceleration var movementInfo = new List <string> { "Relative Speed: " + UnitDisplay.Speed(targetVelocity, false), "Relative Acceleration: " + UnitDisplay.Acceleration(target.GetRelativeAcceleration().Length()), }; // Add angle of attack if it exists if (targetSpaceCraft != null) { movementInfo.Add("Angle of Attack: " + UnitDisplay.Degrees(targetSpaceCraft.GetAlpha())); } _textDisplay.AddTextBlock(StringAlignment.Near, movementInfo); var forceInfo = new List <string> { "Mass: " + UnitDisplay.Mass(target.Mass) }; // Add additional forces if (targetSpaceCraft != null) { DVector2 dragForce = targetSpaceCraft.AccelerationD * targetSpaceCraft.Mass; DVector2 liftForce = targetSpaceCraft.AccelerationL * targetSpaceCraft.Mass * Math.Cos(targetSpaceCraft.Roll); forceInfo.Add("Thrust: " + UnitDisplay.Force(targetSpaceCraft.Thrust)); forceInfo.Add("Drag: " + UnitDisplay.Force(dragForce.Length())); forceInfo.Add("Lift: " + UnitDisplay.Force(liftForce.Length())); } _textDisplay.AddTextBlock(StringAlignment.Near, forceInfo); // Don't show apogee/perigee info for the sun if (!(target is Sun)) { _textDisplay.AddTextBlock(StringAlignment.Near, new List <string> { "Apogee: " + UnitDisplay.Distance(target.Apogee), "Perigee: " + UnitDisplay.Distance(target.Perigee) }); } // Add atmospheric info if the spaceship is the target if (targetSpaceCraft != null) { double density = targetSpaceCraft.GravitationalParent.GetAtmosphericDensity(target.GetRelativeAltitude()); double dynamicPressure = 0.5 * density * targetVelocity * targetVelocity; _textDisplay.AddTextBlock(StringAlignment.Near, new List <string> { "Air Density: " + UnitDisplay.Density(density), "Dynamic Pressure: " + UnitDisplay.Pressure(dynamicPressure), "Heating Rate: " + UnitDisplay.Heat(targetSpaceCraft.HeatingRate) }); } _textDisplay.Draw(graphics); } }
/// <summary> /// Draws all the physics bodies and UI elements. /// </summary> private unsafe void DrawFrame(TimeStep timeStep, FpsManager frameTimer) { var font = new Font("Verdana Bold", 14); var brush = new SolidBrush(Color.White); RectangleD cameraBounds = _camera.GetBounds(); IGravitationalBody target = _gravitationalBodies[_targetIndex]; var targetSpaceCraft = target as SpaceCraftBase; // If openCL is supported render all cl bodies if (_renderingType == RenderingType.OpenCLHardware || _renderingType == RenderingType.OpenCLSoftware) { _gpuClear.RenderCl(_clProxy); foreach (MassiveBodyBase renderable in _massiveBodies) { if (renderable.Visibility(cameraBounds) > 0) { renderable.RenderCl(_clProxy, cameraBounds, _sun); } } int[] frameData = _clProxy.ReadIntBuffer("image", RenderUtils.ScreenArea); var rect = new Rectangle(0, 0, _imageBitmap.Width, _imageBitmap.Height); BitmapData bmpData = _imageBitmap.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); Marshal.Copy(frameData, 0, bmpData.Scan0, RenderUtils.ScreenArea); var ptr = (byte *)bmpData.Scan0; // Hack to force full alpha for now for (int i = 0; i < RenderUtils.ScreenArea; i++) { ptr[i * 4 + 3] = 255; } _imageBitmap.UnlockBits(bmpData); } else { // Fall back to gdi for cl renderables using (var graphics = Graphics.FromImage(_imageBitmap)) { graphics.Clear(Color.Black); foreach (MassiveBodyBase renderable in _massiveBodies) { if (renderable.Visibility(cameraBounds) > 0) { renderable.RenderGdiFallback(graphics, cameraBounds, _sun); } } } } // Draw all orbit traces, spacecrafts, and GDI objects using (var graphics = Graphics.FromImage(_imageBitmap)) { graphics.SmoothingMode = SmoothingMode.HighSpeed; graphics.PixelOffsetMode = PixelOffsetMode.HighSpeed; graphics.CompositingQuality = CompositingQuality.HighSpeed; graphics.InterpolationMode = InterpolationMode.NearestNeighbor; RenderUtils.DrawLine(graphics, cameraBounds, new DVector2(0, -10e12), new DVector2(0, 10e12), Color.FromArgb(40, 255, 255, 255)); RenderUtils.DrawLine(graphics, cameraBounds, new DVector2(-10e12, 0), new DVector2(10e12, 0), Color.FromArgb(40, 255, 255, 255)); double apogee = 0; double perigee = 0; // Draw orbit traces foreach (MassiveBodyBase massiveBody in _massiveBodies) { if (massiveBody is Sun) { continue; } OrbitTrace trace = OrbitHelper.TraceMassiveBody(massiveBody); if (target == massiveBody) { apogee = trace.Apogee; perigee = trace.Perigee; } trace.Draw(graphics, cameraBounds, massiveBody); } // Draw structures foreach (StructureBase structure in _structures) { structure.RenderGdi(graphics, cameraBounds); } // Draw spacecraft foreach (SpaceCraftBase spaceCraft in _spaceCrafts) { if (spaceCraft.Visibility(cameraBounds) > 0) { RectangleD bounds = spaceCraft.ComputeBoundingBox(); // In range for render if (cameraBounds.IntersectsWith(bounds)) { spaceCraft.RenderGdi(graphics, cameraBounds); } } if (spaceCraft.Parent != null) { continue; } OrbitTrace trace = OrbitHelper.TraceSpaceCraft(spaceCraft); if (target == spaceCraft) { apogee = trace.Apogee; perigee = trace.Perigee; } trace.Draw(graphics, cameraBounds, spaceCraft); } var elapsedTime = TimeSpan.FromSeconds(_totalElapsedSeconds); int elapsedYears = elapsedTime.Days / 365; int elapsedDays = elapsedTime.Days % 365; graphics.DrawString("Elapsed Time: " + string.Format("Y: {0} D: {1} H: {2} M: {3} S: {4}", elapsedYears, elapsedDays, elapsedTime.Hours, elapsedTime.Minutes, elapsedTime.Seconds), font, brush, 5, 5); graphics.DrawString("Update Speed: " + timeStep.Multiplier + " X", font, brush, 5, 35); double altitude = target.GetRelativeAltitude(); graphics.DrawString("Altitude: " + UnitDisplay.Distance(altitude), font, brush, 5, 90); graphics.DrawString(string.Format("Target: {0}", target), font, brush, RenderUtils.ScreenWidth / 2.0f, 5, new StringFormat { Alignment = StringAlignment.Center }); double targetVelocity = target.GetRelativeVelocity().Length(); graphics.DrawString("Relative Speed: " + UnitDisplay.Speed(targetVelocity, false), font, brush, 5, 175); graphics.DrawString("Relative Acceleration: " + UnitDisplay.Acceleration(target.GetRelativeAcceleration().Length()), font, brush, 5, 205); graphics.DrawString("Apogee: " + UnitDisplay.Distance(apogee), font, brush, 5, 345); graphics.DrawString("Perigee: " + UnitDisplay.Distance(perigee), font, brush, 5, 375); graphics.DrawString("Mass: " + UnitDisplay.Mass(target.Mass), font, brush, 5, 260); if (targetSpaceCraft != null) { double downrangeDistance = targetSpaceCraft.GetDownrangeDistance(_strongback.Position); graphics.DrawString("Downrange: " + UnitDisplay.Distance(downrangeDistance), font, brush, 5, 120); graphics.DrawString("Thrust: " + UnitDisplay.Force(targetSpaceCraft.Thrust), font, brush, 5, 290); double density = targetSpaceCraft.GravitationalParent.GetAtmosphericDensity(altitude); graphics.DrawString("Air Density: " + UnitDisplay.Density(density), font, brush, 5, 430); double dynamicPressure = 0.5 * density * targetVelocity * targetVelocity; graphics.DrawString("Dynamic Pressure: " + UnitDisplay.Pressure(dynamicPressure), font, brush, 5, 460); } graphics.DrawString("FPS: " + frameTimer.CurrentFps, font, brush, RenderUtils.ScreenWidth - 80, 5); } // Draw all GUI elements (higher quality) using (var graphics = Graphics.FromImage(_imageBitmap)) { graphics.SmoothingMode = SmoothingMode.HighQuality; double throttle = 0; if (targetSpaceCraft != null) { throttle = targetSpaceCraft.Throttle; } foreach (IGauge gauge in _gauges) { if (targetSpaceCraft != null) { gauge.Update(_gravitationalBodies[_targetIndex].Rotation, throttle / 100.0); } gauge.Render(graphics, cameraBounds); } _eventManager.Render(graphics); } }
/// <summary> /// Draws all the physics bodies and UI elements. /// </summary> private unsafe void DrawFrame(TimeStep timeStep, FpsManager frameTimer) { _textDisplay.Clear(); // check for global events _eventManager.CheckForGlobalEvents(this); RectangleD cameraBounds = _camera.Bounds; IGravitationalBody target = _gravitationalBodies[_targetIndex]; var targetSpaceCraft = target as SpaceCraftBase; // If openCL is supported render all cl bodies if (_renderingType == RenderingType.OpenCLHardware || _renderingType == RenderingType.OpenCLSoftware) { _gpuClear.RenderCl(_clProxy); foreach (MassiveBodyBase renderable in _massiveBodies) { if (renderable.Visibility(cameraBounds) > 0) { renderable.RenderCl(_clProxy, _camera, _sun); } } int[] frameData = _clProxy.ReadIntBuffer("image", RenderUtils.ScreenArea); var rect = new Rectangle(0, 0, _imageBitmap.Width, _imageBitmap.Height); BitmapData bmpData = _imageBitmap.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); Marshal.Copy(frameData, 0, bmpData.Scan0, RenderUtils.ScreenArea); var ptr = (byte *)bmpData.Scan0; // Hack to force full alpha for now for (int i = 0; i < RenderUtils.ScreenArea; i++) { ptr[i * 4 + 3] = 255; } _imageBitmap.UnlockBits(bmpData); } else { // Fall back to gdi for cl renderables using (var graphics = Graphics.FromImage(_imageBitmap)) { graphics.Clear(Color.Black); _camera.ApplyScreenRotation(graphics); foreach (MassiveBodyBase renderable in _massiveBodies) { if (renderable.Visibility(cameraBounds) > 0) { renderable.RenderGdiFallback(graphics, _camera, _sun); } } } } // Draw all orbit traces, spacecrafts, and GDI objects using (Graphics graphics = RenderUtils.GetContext(false, _imageBitmap)) { _camera.ApplyScreenRotation(graphics); // Draw all massive body orbit traces foreach (MassiveBodyBase massiveBody in _massiveBodies) { if (massiveBody is Sun) { continue; } massiveBody.RenderGdi(graphics, _camera); } graphics.ResetTransform(); // Draw structures foreach (StructureBase structure in _structures) { structure.RenderGdi(graphics, _camera); } _spaceCraftManager.Render(graphics, _camera); } // Draw all GUI elements (higher quality) using (Graphics graphics = RenderUtils.GetContext(true, _imageBitmap)) { double throttle = 0; if (targetSpaceCraft != null) { throttle = targetSpaceCraft.Throttle; // TODO: render PIP //int width = RenderUtils.ScreenWidth; //int height = RenderUtils.ScreenHeight; //Rectangle rectPip = new Rectangle(width - 220, 100, 200, height - 300); //graphics.DrawRectangle(Pens.White, rectPip); //_pipCam.ApplyScreenRotation(graphics); //_spaceCraftManager.Render(graphics, _pipCam); } double pitch = 0.0; double flightPathAngle = 0.0; foreach (IGauge gauge in _gauges) { if (targetSpaceCraft != null) { if (_targetInOrbit && _rotateInOrbit) { pitch = _gravitationalBodies[_targetIndex].Pitch; } else { pitch = _gravitationalBodies[_targetIndex].GetRelativePitch(); } flightPathAngle = pitch - targetSpaceCraft.GetAlpha(); gauge.Update(pitch, throttle / 100.0, flightPathAngle); } gauge.Render(graphics, cameraBounds); } _eventManager.Render(graphics); var elapsedTime = TimeSpan.FromSeconds(_totalElapsedSeconds - _clockDelay); int elapsedYears = elapsedTime.Days / 365; int elapsedDays = elapsedTime.Days % 365; DateTime localTime = _originTime + elapsedTime; // Main timing display _textDisplay.AddTextBlock(StringAlignment.Near, new List <string> { //$"Origin Time: {localTime.ToShortDateString()} {localTime.ToShortTimeString()}", $"Origin Time: {string.Format("{0}-{1}-{2:D2}", localTime.Date.Year, localTime.Date.Month, localTime.Date.Day)} {localTime.ToShortTimeString()}", //$"Elapsed Time: Y:{elapsedYears} D:{elapsedDays} H:{elapsedTime.Hours} M:{elapsedTime.Minutes} S:{Math.Round(elapsedTime.TotalSeconds % 60)}", $"Elapsed Time: Y:{elapsedYears} D:{elapsedDays} H:{elapsedTime.Hours} M:{elapsedTime.Minutes} S:{elapsedTime.Seconds}", $"Update Speed: {timeStep.Multiplier}" }); // Target display _textDisplay.AddTextBlock(StringAlignment.Center, string.Format("Target: {0}", target)); // FPS _textDisplay.AddTextBlock(StringAlignment.Far, "FPS: " + frameTimer.CurrentFps); double targetVelocity = target.GetRelativeVelocity().Length(); double inertialVelocity = target.GetInertialVelocity().Length(); // Info for altitude // double altitude = target.GetRelativeAltitude(); // double altitude = target.GetRelativeAltitude() + 82.5; // Starship Mk1 double altitude = target.GetRelativeAltitude() - 111.4; // Starship + Super Heavy var altitudeInfo = new List <string> { "Altitude: " + UnitDisplay.Distance(altitude) }; // Add downrange if spacecraft exists if (targetSpaceCraft != null) { double downrangeDistance = targetSpaceCraft.GetDownrangeDistance(_structures[0].Position); altitudeInfo.Add("Downrange: " + UnitDisplay.Distance(downrangeDistance)); } _textDisplay.AddTextBlock(StringAlignment.Near, altitudeInfo); // Info for speed / acceleration var movementInfo = new List <string> { "Inertial Velocity: " + UnitDisplay.Speed(targetVelocity, useKmh), "Orbital Velocity: " + UnitDisplay.Speed(inertialVelocity, useKmh), "Inertial Acceleration: " + UnitDisplay.Acceleration(target.GetRelativeAcceleration().Length()), "Orbital Acceleration: " + UnitDisplay.Acceleration(target.GetInertialAcceleration().Length()) }; double lateralVelocity = target.GetLateralVelocity().Length(); if (lateralVelocity > 0) { double lateralPosition = target.GetLateralPosition().Length(); altitudeInfo.Add("Crossrange: " + UnitDisplay.Distance(lateralPosition)); movementInfo.Add("Lateral Velocity: " + UnitDisplay.Speed(lateralVelocity, useKmh)); movementInfo.Add("Lateral Acceleration: " + UnitDisplay.Acceleration(target.GetLateralAcceleration().Length())); } // Add angle of attack if it exists if (targetSpaceCraft != null) { movementInfo.Add("Angle of Attack: " + UnitDisplay.AoA(targetSpaceCraft.GetAlpha())); // calculate the current inclination double G = 6.67408E-11; double M = target.GravitationalParent.Mass; double μ = G * M; double R = target.GravitationalParent.SurfaceRadius; double apogee = target.Apoapsis; if (initialPerigee == 0) { initialPerigee = target.Periapsis; } double Ra = R + apogee; double Rp = R + initialPerigee; double e = 1 - 2 / ((Ra / Rp) + 1); double ω = 0; double f = 0; // orbital period double a = (initialPerigee + R * 2 + apogee) / 2; double P = 2 * Math.PI * Math.Pow(Math.Pow(a, 3) / μ, 0.5); double n = 2 * Math.PI / P; double Δi = 2 * Math.Asin(lateralVelocity * (1 + e * Math.Cos(f)) / (Math.Pow(1 - Math.Pow(e, 2), 0.5) * Math.Cos(ω + f) * n * a * 2)); double inclination = launchInclination * MathHelper.DegreesToRadians - Δi; movementInfo.Add("Inclination: " + UnitDisplay.Degrees(inclination)); double speedOfSound = targetSpaceCraft.GravitationalParent.GetSpeedOfSound(target.GetRelativeAltitude()); if (speedOfSound > 0) { double machNumber = targetVelocity / speedOfSound; movementInfo.Add("Mach number: " + UnitDisplay.Mach(machNumber)); } } _textDisplay.AddTextBlock(StringAlignment.Near, movementInfo); var forceInfo = new List <string> { "Mass: " + UnitDisplay.Mass(target.Mass) }; // Add additional forces if (targetSpaceCraft != null) { DVector2 dragForce = targetSpaceCraft.AccelerationD * targetSpaceCraft.Mass; DVector2 liftForce = targetSpaceCraft.AccelerationL * targetSpaceCraft.Mass; if (Settings.Default.UseTheTurnForce) { liftForce *= Math.Cos(targetSpaceCraft.Roll); } forceInfo.Add("Thrust: " + UnitDisplay.Force(targetSpaceCraft.Thrust)); forceInfo.Add("Drag: " + UnitDisplay.Force(dragForce.Length())); forceInfo.Add("Lift: " + UnitDisplay.Force(liftForce.Length())); } _textDisplay.AddTextBlock(StringAlignment.Near, forceInfo); // Don't show Apoapsis/Periapsis info for the sun if (!(target is Sun) && target.GravitationalParent != null) { _textDisplay.AddTextBlock(StringAlignment.Near, new List <string> { $"{target.GravitationalParent.ApoapsisName}: {UnitDisplay.Distance(target.Apoapsis)}", $"{target.GravitationalParent.PeriapsisName}: {UnitDisplay.Distance(target.Periapsis)}", }); } // Add atmospheric info if the spaceship is the target if (targetSpaceCraft != null && targetSpaceCraft.GravitationalParent != null) { double density = targetSpaceCraft.GravitationalParent.GetAtmosphericDensity(target.GetRelativeAltitude()); double dynamicPressure = 0.5 * density * targetVelocity * targetVelocity; _textDisplay.AddTextBlock(StringAlignment.Near, new List <string> { "Air Density: " + UnitDisplay.Density(density), "Dynamic Pressure: " + UnitDisplay.Pressure(dynamicPressure), "Heat Flux: " + UnitDisplay.Heat(targetSpaceCraft.HeatingRate) }); } _textDisplay.Draw(graphics); } }