diff --git a/RasterPropMonitor/Core/MonitorPage.cs b/RasterPropMonitor/Core/MonitorPage.cs index 77da5c49..d2ac410e 100644 --- a/RasterPropMonitor/Core/MonitorPage.cs +++ b/RasterPropMonitor/Core/MonitorPage.cs @@ -33,6 +33,7 @@ public class MonitorPage public readonly int pageNumber; public readonly string name = string.Empty; public readonly bool unlocker; + private const int INVALID_BUTTON = -1; private string text; private StringProcessorFormatter[] spf; private string[] outputLines; @@ -80,7 +81,8 @@ public enum BackgroundType private readonly MonoBehaviour backgroundHandlerModule, pageHandlerModule; private readonly List techsRequired = new List(); private readonly string fallbackPageName = string.Empty; - + private readonly int buttonNextPatch = INVALID_BUTTON; + private readonly int buttonPrevPatch = INVALID_BUTTON; private struct HandlerSupportMethods { @@ -433,6 +435,16 @@ public MonitorPage(int idNum, ConfigNode node, RasterPropMonitor thatMonitor) } } + int intValue = INVALID_BUTTON; + if (node.TryGetValue("buttonNextPatch", ref intValue)) + { + buttonNextPatch = intValue; + } + + if (node.TryGetValue("buttonPrevPatch", ref intValue)) + { + buttonPrevPatch = intValue; + } } private static MethodInfo InstantiateHandler(ConfigNode node, RasterPropMonitor ourMonitor, out MonoBehaviour moduleInstance, out HandlerSupportMethods support) @@ -622,7 +634,7 @@ public void Active(bool state) public bool GlobalButtonClick(int buttonID) { buttonID = redirectGlobals[buttonID] ?? buttonID; - if (buttonID == -1) + if (buttonID == INVALID_BUTTON) { return false; } @@ -637,6 +649,16 @@ public bool GlobalButtonClick(int buttonID) backgroundHandlerS.buttonClick(buttonID); actionTaken = true; } + if (buttonID == buttonNextPatch) + { + ourMonitor.SelectNextPatch(); + actionTaken = true; + } + if (buttonID == buttonPrevPatch) + { + ourMonitor.SelectPreviousPatch(); + actionTaken = true; + } return actionTaken; } diff --git a/RasterPropMonitor/Core/OrbitExtensions.cs b/RasterPropMonitor/Core/OrbitExtensions.cs index 2159053c..91e189ce 100644 --- a/RasterPropMonitor/Core/OrbitExtensions.cs +++ b/RasterPropMonitor/Core/OrbitExtensions.cs @@ -45,14 +45,7 @@ public static Vector3d SwappedOrbitNormal(this Orbit o) //occurs at a true anomaly that a does not actually ever attain public static double TimeOfAscendingNode(this Orbit a, Orbit b, double UT) { - if (a.eccentricity >= 1.0) - { - return UT; - } - else - { - return a.TimeOfTrueAnomaly(Orbit.AscendingNodeTrueAnomaly(a, b), UT); - } + return a.TimeOfTrueAnomaly(Orbit.AscendingNodeTrueAnomaly(a, b), UT); } //Returns the next time at which a will cross its descending node with b. //For elliptical orbits this is a time between UT and UT + a.period. @@ -62,14 +55,7 @@ public static double TimeOfAscendingNode(this Orbit a, Orbit b, double UT) //occurs at a true anomaly that a does not actually ever attain public static double TimeOfDescendingNode(this Orbit a, Orbit b, double UT) { - if (a.eccentricity >= 1.0) - { - return UT; - } - else - { - return a.TimeOfTrueAnomaly(Orbit.DescendingNodeTrueAnomaly(a, b), UT); - } + return a.TimeOfTrueAnomaly(Orbit.DescendingNodeTrueAnomaly(a, b), UT); } //Returns the next time at which the orbiting object will cross the equator //moving northward, if o is east-moving, or southward, if o is west-moving. @@ -80,14 +66,7 @@ public static double TimeOfDescendingNode(this Orbit a, Orbit b, double UT) //"ascending node" occurs at a true anomaly that o does not actually ever attain. public static double TimeOfAscendingNodeEquatorial(this Orbit o, double UT) { - if (o.eccentricity >= 1.0) - { - return UT; - } - else - { - return o.TimeOfTrueAnomaly(o.AscendingNodeEquatorialTrueAnomaly(), UT); - } + return o.TimeOfTrueAnomaly(o.AscendingNodeEquatorialTrueAnomaly(), UT); } //Returns the next time at which the orbiting object will cross the equator //moving southward, if o is east-moving, or northward, if o is west-moving. diff --git a/RasterPropMonitor/Core/RPMCEvaluators.cs b/RasterPropMonitor/Core/RPMCEvaluators.cs index 10192cde..0ad06435 100644 --- a/RasterPropMonitor/Core/RPMCEvaluators.cs +++ b/RasterPropMonitor/Core/RPMCEvaluators.cs @@ -319,7 +319,22 @@ internal VariableEvaluator GetEvaluator(string input, out VariableUpdateType upd { return ScienceUtil.GetExperimentBiome(vessel.mainBody, vessel.latitude, vessel.longitude); }; - + case "PATCHENCOUNTERBODY": + return (RPMVesselComputer comp) => + { + if (!JUtil.OrbitMakesSense(vessel)) return string.Empty; + Orbit patch = GetSelectedPatchOrbit(); + switch (patch.patchEndTransition) + { + case Orbit.PatchTransitionType.ENCOUNTER: + return patch.nextPatch.referenceBody.bodyName; + case Orbit.PatchTransitionType.ESCAPE: + return patch.referenceBody.bodyName; + } + return string.Empty; + }; + case "PATCHORBITBODY": + return (RPMVesselComputer comp) => GetSelectedPatchOrbit().referenceBody.name; } return null; @@ -1600,6 +1615,8 @@ internal NumericVariableEvaluator GetNumericEvaluator(string input, out Variable } return double.NaN; }; + case "ANYTARGETEXISTS": + return (RPMVesselComputer comp) => comp.target == null ? -1d : 1d; case "TARGETEXISTS": return (RPMVesselComputer comp) => { @@ -2626,6 +2643,138 @@ internal NumericVariableEvaluator GetNumericEvaluator(string input, out Variable } return -1d; }; + case "PATCHINDEX": + return (RPMVesselComputer comp) => + { + (int index, Orbit _) = GetSelectedPatch(); + return index + 1; + }; + case "PATCHCOUNT": + return (RPMVesselComputer comp) => + { + (int index, Orbit _) = GetLastPatch(); + return index + 1; + }; + case "PATCHALTITUDE": + return (RPMVesselComputer comp) => GetSelectedPatchOrbit().referenceBody.GetAltitude(vessel.CoM); + case "PATCHORBTSPEED": + return (RPMVesselComputer comp) => GetSelectedPatchOrbit().GetVel().magnitude; + case "PATCHAPOAPSIS": + return (RPMVesselComputer comp) => JUtil.OrbitMakesSense(vessel) ? GetSelectedPatchOrbit().ApA : double.NaN; + case "PATCHPERIAPSIS": + return (RPMVesselComputer comp) => JUtil.OrbitMakesSense(vessel) ? GetSelectedPatchOrbit().PeA : double.NaN; + case "PATCHINCLINATION": + return (RPMVesselComputer comp) => JUtil.OrbitMakesSense(vessel) ? GetSelectedPatchOrbit().inclination : double.NaN; + case "PATCHECCENTRICITY": + return (RPMVesselComputer comp) => JUtil.OrbitMakesSense(vessel) ? GetSelectedPatchOrbit().eccentricity : double.NaN; + case "PATCHTIMETOAPSECS": + return (RPMVesselComputer comp) => + { + if (!JUtil.OrbitMakesSense(vessel)) return double.NaN; + Orbit patch = GetSelectedPatchOrbit(); + // When the tracking station is not upgraded, StartUT will not be updated to the current time. + if (patch.StartUT == 0.0) return patch.timeToAp; + return patch.timeToAp + patch.StartUT - Planetarium.GetUniversalTime(); + }; + case "PATCHTIMETOPESECS": + return (RPMVesselComputer comp) => + { + if (!JUtil.OrbitMakesSense(vessel)) return double.NaN; + Orbit patch = GetSelectedPatchOrbit(); + // When the tracking station is not upgraded, StartUT will not be updated to the current time. + if (patch.StartUT == 0.0) return patch.timeToPe; + return patch.timeToPe + patch.StartUT - Planetarium.GetUniversalTime(); + }; + case "PATCHORBPERIODSECS": + return (RPMVesselComputer comp) => JUtil.OrbitMakesSense(vessel) ? GetSelectedPatchOrbit().period : double.NaN; + case "PATCHTIMETOANEQUATORIAL": + return (RPMVesselComputer comp) => + { + Orbit patch = GetSelectedPatchOrbit(); + if (!JUtil.OrbitMakesSense(vessel) || !patch.AscendingNodeEquatorialExists()) + { + return double.NaN; + } + return patch.TimeOfAscendingNodeEquatorial(Planetarium.GetUniversalTime()) - Planetarium.GetUniversalTime(); + }; + case "PATCHTIMETODNEQUATORIAL": + return (RPMVesselComputer comp) => + { + Orbit patch = GetSelectedPatchOrbit(); + if (!JUtil.OrbitMakesSense(vessel) || !patch.DescendingNodeEquatorialExists()) + { + return double.NaN; + } + return patch.TimeOfDescendingNodeEquatorial(Planetarium.GetUniversalTime()) - Planetarium.GetUniversalTime(); + }; + case "PATCHFIRST": + return (RPMVesselComputer comp) => + { + (int index, Orbit _) = GetSelectedPatch(); + return index == 0 ? 1.0d : 0.0d; + }; + case "PATCHLAST": + return (RPMVesselComputer comp) => + { + (int selected, Orbit _) = GetSelectedPatch(); + (int last, Orbit _) = GetLastPatch(); + return selected == last ? 1.0d : 0.0d; + }; + case "PATCHNEXTAPSISTYPE": + return (RPMVesselComputer comp) => + { + Orbit patch = GetSelectedPatchOrbit(); + if (patch.eccentricity < 1.0) + { + // Which one will we reach first? + return (patch.timeToPe < patch.timeToAp) ? -1.0 : 1.0; + } + + // Ship is hyperbolic. There is no Ap. Have we already + // passed Pe? + return (patch.timeToPe > 0.0) ? -1.0 : 0.0; + }; + case "PATCHNEXT_ANDN_EQUATORIAL": + return (RPMVesselComputer comp) => + { + Orbit patch = GetSelectedPatchOrbit(); + double universalTime = Planetarium.GetUniversalTime(); + if (!JUtil.OrbitMakesSense(vessel)) return 0.0; + + double dnTime = patch.DescendingNodeEquatorialExists() ? patch.TimeOfDescendingNodeEquatorial(universalTime) : double.NaN; + double anTime = patch.AscendingNodeEquatorialExists() ? patch.TimeOfAscendingNodeEquatorial(universalTime) : double.NaN; + + if (double.IsNaN(anTime)) return -1.0; + if (double.IsNaN(dnTime)) return 1.0; + return Math.Max(0.0, anTime) < Math.Max(dnTime, 0.0) ? 1.0 : -1.0; + }; + case "PATCHENCOUNTEREXISTS": + return (RPMVesselComputer comp) => + { + if (!JUtil.OrbitMakesSense(vessel)) return 0.0; + Orbit patch = GetSelectedPatchOrbit(); + switch (patch.patchEndTransition) + { + case Orbit.PatchTransitionType.ESCAPE: + return -1d; + case Orbit.PatchTransitionType.ENCOUNTER: + return 1d; + default: + return 0.0d; + } + }; + case "PATCHENCOUNTERTIME": + return (RPMVesselComputer comp) => + { + if (!JUtil.OrbitMakesSense(vessel)) return 0.0; + Orbit patch = GetSelectedPatchOrbit(); + if (patch.patchEndTransition == Orbit.PatchTransitionType.ENCOUNTER || + patch.patchEndTransition == Orbit.PatchTransitionType.ESCAPE) + { + return patch.UTsoi - Planetarium.GetUniversalTime(); + } + return 0.0; + }; } return null; diff --git a/RasterPropMonitor/Core/RasterPropMonitor.cs b/RasterPropMonitor/Core/RasterPropMonitor.cs index d95d5465..a8410907 100644 --- a/RasterPropMonitor/Core/RasterPropMonitor.cs +++ b/RasterPropMonitor/Core/RasterPropMonitor.cs @@ -379,6 +379,16 @@ public void PageButtonClick(MonitorPage triggeredPage) } } + internal void SelectNextPatch() + { + rpmComp.SelectNextPatch(); + } + + internal void SelectPreviousPatch() + { + rpmComp.SelectPreviousPatch(); + } + // Update according to the given refresh rate. private bool UpdateCheck() { @@ -508,8 +518,8 @@ public void LateUpdate() firstRenderComplete = false; textRefreshRequired = true; } - else if (!activePage.isMutable) - { + else if (!activePage.isMutable) + { // In case the page is empty and has no camera, the screen is treated as turned off and blanked once. if (!firstRenderComplete) { diff --git a/RasterPropMonitor/Core/RasterPropMonitorComputer.cs b/RasterPropMonitor/Core/RasterPropMonitorComputer.cs index 09a914aa..cbcbae9a 100644 --- a/RasterPropMonitor/Core/RasterPropMonitorComputer.cs +++ b/RasterPropMonitor/Core/RasterPropMonitorComputer.cs @@ -111,6 +111,7 @@ internal PeriodicRandomValue(int period_) private ExternalVariableHandlers plugins = null; internal Dictionary overrideColors = new Dictionary(); + private int selectedPatchIndex; static readonly Regex x_agmemoRegex = new Regex("^AG([0-9])\\s*=\\s*(.*)\\s*"); @@ -151,7 +152,7 @@ public void RegisterVariableCallback(string variableName, Action cb) { var vc = InstantiateVariableOrNumber(variableName); vc.onChangeCallbacks += cb; - cb((float)vc.numericValue); + cb(vc.AsFloat()); } /// @@ -332,6 +333,69 @@ public void RestoreInternalModule(InternalModule module) modulesToRestore.Add(module); } + /// + /// Find the selected orbital patch. A patch is selected if we are + /// looking at it. + /// + /// + /// 1. The count of the patch. 0 for current orbit, 1 for next SOI, and + /// so on + /// 2. The orbit object that represents the patch. + /// + internal (int, Orbit) GetSelectedPatch() + { + return EffectivePatch(selectedPatchIndex); + } + + private Orbit GetSelectedPatchOrbit() + { + (int _, Orbit patch) = GetSelectedPatch(); + return patch; + } + + internal (int, Orbit) GetLastPatch() + { + return EffectivePatch(1000); + } + + internal void SelectNextPatch() + { + (int effectivePatchIndex, _) = GetSelectedPatch(); + SelectPatch(effectivePatchIndex + 1); + } + + internal void SelectPreviousPatch() + { + (int effectivePatchIndex, _) = GetSelectedPatch(); + SelectPatch(effectivePatchIndex - 1); + } + + private void SelectPatch(int patchIndex) + { + (int effectivePatchIndex, _) = EffectivePatch(patchIndex); + selectedPatchIndex = effectivePatchIndex; + } + + /// + /// Returns the orbit (patch) and orbit index given a selection. + /// + /// true if it's time to update things + private (int, Orbit) EffectivePatch(int patchIndex) + { + Orbit patch = vessel.orbit; + int effectivePatchIndex = 0; + while (effectivePatchIndex < patchIndex + && patch.nextPatch != null + && patch.nextPatch.activePatch + && (patch.patchEndTransition == Orbit.PatchTransitionType.ENCOUNTER || patch.patchEndTransition == Orbit.PatchTransitionType.ESCAPE)) + { + patch = patch.nextPatch; + effectivePatchIndex++; + } + + return (effectivePatchIndex, patch); + } + #region Monobehaviour /// /// Configure this computer for operation. diff --git a/RasterPropMonitor/Handlers/JSIOrbitDisplay.cs b/RasterPropMonitor/Handlers/JSIOrbitDisplay.cs index dbda4b08..ef6508f7 100644 --- a/RasterPropMonitor/Handlers/JSIOrbitDisplay.cs +++ b/RasterPropMonitor/Handlers/JSIOrbitDisplay.cs @@ -129,12 +129,12 @@ static Vector3 ScreenPositionFromOrbitAtTA(Orbit o, CelestialBody referenceBody, private static void DrawOrbit(Orbit o, CelestialBody referenceBody, Matrix4x4 screenTransform) { - int numSegments = ORBIT_POINTS; - + const double MIN_POINTS = 4; if (!o.activePatch) { return; } + double startTA; double endTA; double now = Planetarium.GetUniversalTime(); @@ -157,21 +157,46 @@ private static void DrawOrbit(Orbit o, CelestialBody referenceBody, Matrix4x4 sc startTA = o.GetUTforTrueAnomaly(0.0, now); endTA = startTA + 2.0 * Math.PI; } - double dTheta = (endTA - startTA) / (double)numSegments; + + double dTheta = (endTA - startTA) / MIN_POINTS; double theta = startTA; - double bias = Mathf.Lerp(0, -0.5f, (float)o.eccentricity); Vector3 lastVertex = ScreenPositionFromOrbitAtTA(o, referenceBody, screenTransform, theta, now); - for (int i = 0; i < numSegments; ++i) + for (int i = 0; i < MIN_POINTS; ++i) { - GL.Vertex3(lastVertex.x, lastVertex.y, 0.0f); - theta += dTheta; - double ta = theta + bias * Math.Sin(theta * 2); + double nextTheta = theta + dTheta; + Vector3 nextVertex = ScreenPositionFromOrbitAtTA(o, referenceBody, screenTransform, nextTheta, now); + DrawOrbitSegment(o, referenceBody, screenTransform, now, theta, lastVertex, nextTheta, nextVertex); + lastVertex = nextVertex; + theta = nextTheta; + } + } - Vector3 newVertex = ScreenPositionFromOrbitAtTA(o, referenceBody, screenTransform, ta, now); - GL.Vertex3(newVertex.x, newVertex.y, 0.0f); + private static void DrawOrbitSegment( + Orbit o, + CelestialBody referenceBody, + Matrix4x4 screenTransform, + double now, + double startTA, + Vector3 startVertex, + double endTA, + Vector3 endVertex) + { + double midTA = (startTA + endTA) / 2.0; + Vector3 midVertex = ScreenPositionFromOrbitAtTA(o, referenceBody, screenTransform, midTA, now); + Vector3 midStraight = (startVertex + endVertex) * 0.5f; + // Debug.Log($"startTA: {startTA}, endTA: {endTA}, startVertex: {startVertex}, endVertex: {endVertex}, midVertex: {midVertex}, midStraight: {midStraight}"); - lastVertex = newVertex; + if (Math.Abs(startTA - endTA) < 0.01 || (midStraight - midVertex).sqrMagnitude < 9.0) + { + GL.Vertex3(startVertex.x, startVertex.y, 0.0f); + GL.Vertex3(midVertex.x, midVertex.y, 0.0f); + GL.Vertex3(midVertex.x, midVertex.y, 0.0f); + GL.Vertex3(endVertex.x, endVertex.y, 0.0f); + return; } + + DrawOrbitSegment(o, referenceBody, screenTransform, now, startTA, startVertex, midTA, midVertex); + DrawOrbitSegment(o, referenceBody, screenTransform, now, midTA, midVertex, endTA, endVertex); } // Fallback method: The orbit should be valid, but it's not showing as @@ -296,6 +321,9 @@ public bool RenderOrbit(RenderTexture screen, float cameraAspect) double horizPixelSize = displayPosition.z - iconPixelSize; double vertPixelSize = displayPosition.w - iconPixelSize; + RasterPropMonitorComputer rpmComp = RasterPropMonitorComputer.FindFromProp(internalProp); + (int patchIndex, Orbit selectedPatch) = rpmComp.GetSelectedPatch(); + // Find a basis for transforming values into the framework of // vessel.orbit. The rendering framework assumes the periapsis // is drawn directly to the right of the mainBody center of mass. @@ -307,12 +335,12 @@ public bool RenderOrbit(RenderTexture screen, float cameraAspect) // the planet's center) into screen space. Matrix4x4 screenTransform = Matrix4x4.identity; double now = Planetarium.GetUniversalTime(); - double timeAtPe = vessel.orbit.GetNextPeriapsisTime(now); + double timeAtPe = selectedPatch.GetNextPeriapsisTime(now); // Get the 3 direction vectors, based on Pe being on the right of the screen // OrbitExtensions provides handy utilities to get these. - Vector3d right = vessel.orbit.Up(timeAtPe); - Vector3d forward = vessel.orbit.SwappedOrbitNormal(); + Vector3d right = selectedPatch.Up(timeAtPe); + Vector3d forward = selectedPatch.SwappedOrbitNormal(); // MOARdV: OrbitExtensions.Horizontal is unstable. I've seen it // become (0, 0, 0) intermittently in flight. Instead, use the // cross product of the other two. @@ -328,85 +356,75 @@ public bool RenderOrbit(RenderTexture screen, float cameraAspect) // Figure out our bounds. First, make sure the entire planet // fits on the screen. We define the center of the vessel.mainBody // as the origin of our coodinate system. - double maxX = vessel.mainBody.Radius; - double minX = -maxX; - double maxY = maxX; - double minY = -maxX; + Bounds bounds = new Bounds(selectedPatch.referenceBody.Radius); - if (vessel.mainBody.atmosphere) + if (selectedPatch.referenceBody.atmosphere) { - maxX += vessel.mainBody.atmosphereDepth; - minX = -maxX; - maxY = maxX; - minY = -maxX; + double radius = selectedPatch.referenceBody.Radius + selectedPatch.referenceBody.atmosphereDepth; + bounds.Add(radius, radius); + bounds.Add(-radius, -radius); } // Now make sure the entire orbit fits on the screen. Vector3 vesselPos; // The PeR, ApR, and semiMinorAxis are all one dimensional, so we // can just apply them directly to these values. - maxX = Math.Max(maxX, vessel.orbit.PeR); - if (vessel.orbit.eccentricity < 1.0) - { - minX = Math.Min(minX, -vessel.orbit.ApR); + bounds.Add(selectedPatch.PeR, 0); - maxY = Math.Max(maxY, vessel.orbit.semiMinorAxis); - minY = Math.Min(minY, -vessel.orbit.semiMinorAxis); + if (selectedPatch.eccentricity < 1.0) + { + bounds.Add(-selectedPatch.ApR, 0); + bounds.Add(0, selectedPatch.semiMinorAxis); + bounds.Add(0, -selectedPatch.semiMinorAxis); } - else if (vessel.orbit.EndUT > 0.0) + + if (selectedPatch.EndUT > 0.0) { // If we're hyperbolic, let's get the SoI transition - vesselPos = screenTransform.MultiplyPoint3x4(vessel.orbit.SwappedRelativePositionAtUT(vessel.orbit.EndUT)); - maxX = Math.Max(maxX, vesselPos.x); - minX = Math.Min(minX, vesselPos.x); - maxY = Math.Max(maxY, vesselPos.y); - minY = Math.Min(minY, vesselPos.y); + vesselPos = screenTransform.MultiplyPoint3x4(selectedPatch.SwappedRelativePositionAtUT(selectedPatch.EndUT)); + bounds.Add(vesselPos.x, vesselPos.y); + } + + if (patchIndex > 0) + { + // Include the start SOI transition + vesselPos = screenTransform.MultiplyPoint3x4(selectedPatch.SwappedRelativePositionAtUT(selectedPatch.StartUT)); + bounds.Add(vesselPos.x, vesselPos.y); } // Make sure the vessel shows up on-screen. Since a hyperbolic // orbit doesn't have a meaningful ApR, we use this as a proxy for // how far we need to extend the bounds to show the vessel. - vesselPos = screenTransform.MultiplyPoint3x4(vessel.orbit.SwappedRelativePositionAtUT(now)); - maxX = Math.Max(maxX, vesselPos.x); - minX = Math.Min(minX, vesselPos.x); - maxY = Math.Max(maxY, vesselPos.y); - minY = Math.Min(minY, vesselPos.y); + if (selectedPatch.referenceBody == vessel.orbit.referenceBody) + { + vesselPos = screenTransform.MultiplyPoint3x4(vessel.orbit.SwappedRelativePositionAtUT(now)); + bounds.Add(vesselPos.x, vesselPos.y); + } // Account for a target vessel var targetBody = FlightGlobals.fetch.VesselTarget as CelestialBody; var targetVessel = FlightGlobals.fetch.VesselTarget as Vessel; - if (targetVessel != null && targetVessel.mainBody != vessel.mainBody) + if (targetVessel != null && targetVessel.mainBody != selectedPatch.referenceBody) { // We only care about tgtVessel if it is in the same SoI. targetVessel = null; } + if (targetVessel != null && !targetVessel.LandedOrSplashed) { - if (targetVessel.mainBody == vessel.mainBody) - { - double tgtPe = targetVessel.orbit.GetNextPeriapsisTime(now); + double tgtPe = targetVessel.orbit.GetNextPeriapsisTime(now); - vesselPos = screenTransform.MultiplyPoint3x4(targetVessel.orbit.SwappedRelativePositionAtUT(tgtPe)); - maxX = Math.Max(maxX, vesselPos.x); - minX = Math.Min(minX, vesselPos.x); - maxY = Math.Max(maxY, vesselPos.y); - minY = Math.Min(minY, vesselPos.y); + vesselPos = screenTransform.MultiplyPoint3x4(targetVessel.orbit.SwappedRelativePositionAtUT(tgtPe)); + bounds.Add(vesselPos.x, vesselPos.y); - if (targetVessel.orbit.eccentricity < 1.0) - { - vesselPos = screenTransform.MultiplyPoint3x4(targetVessel.orbit.SwappedRelativePositionAtUT(targetVessel.orbit.GetNextApoapsisTime(now))); - maxX = Math.Max(maxX, vesselPos.x); - minX = Math.Min(minX, vesselPos.x); - maxY = Math.Max(maxY, vesselPos.y); - minY = Math.Min(minY, vesselPos.y); - } - - vesselPos = screenTransform.MultiplyPoint3x4(targetVessel.orbit.SwappedRelativePositionAtUT(now)); - maxX = Math.Max(maxX, vesselPos.x); - minX = Math.Min(minX, vesselPos.x); - maxY = Math.Max(maxY, vesselPos.y); - minY = Math.Min(minY, vesselPos.y); + if (targetVessel.orbit.eccentricity < 1.0) + { + vesselPos = screenTransform.MultiplyPoint3x4(targetVessel.orbit.SwappedRelativePositionAtUT(targetVessel.orbit.GetNextApoapsisTime(now))); + bounds.Add(vesselPos.x, vesselPos.y); } + + vesselPos = screenTransform.MultiplyPoint3x4(targetVessel.orbit.SwappedRelativePositionAtUT(now)); + bounds.Add(vesselPos.x, vesselPos.y); } if (targetBody != null) @@ -416,15 +434,12 @@ public bool RenderOrbit(RenderTexture screen, float cameraAspect) { targetBody = null; } - else if (targetBody.orbit.referenceBody == vessel.orbit.referenceBody) + else if (targetBody.orbit.referenceBody == selectedPatch.referenceBody) { // If the target body orbits our current world, let's at // least make sure the body's location is visible. vesselPos = screenTransform.MultiplyPoint3x4(targetBody.GetOrbit().SwappedRelativePositionAtUT(now)); - maxX = Math.Max(maxX, vesselPos.x); - minX = Math.Min(minX, vesselPos.x); - maxY = Math.Max(maxY, vesselPos.y); - minY = Math.Min(minY, vesselPos.y); + bounds.Add(vesselPos.x, vesselPos.y); } } @@ -438,38 +453,29 @@ public bool RenderOrbit(RenderTexture screen, float cameraAspect) { double nodePe = node.nextPatch.GetNextPeriapsisTime(now); vesselPos = screenTransform.MultiplyPoint3x4(node.nextPatch.SwappedRelativePositionAtUT(nodePe)); - maxX = Math.Max(maxX, vesselPos.x); - minX = Math.Min(minX, vesselPos.x); - maxY = Math.Max(maxY, vesselPos.y); - minY = Math.Min(minY, vesselPos.y); + bounds.Add(vesselPos.x, vesselPos.y); if (node.nextPatch.eccentricity < 1.0) { double nodeAp = node.nextPatch.GetNextApoapsisTime(now); vesselPos = screenTransform.MultiplyPoint3x4(node.nextPatch.SwappedRelativePositionAtUT(nodeAp)); - maxX = Math.Max(maxX, vesselPos.x); - minX = Math.Min(minX, vesselPos.x); - maxY = Math.Max(maxY, vesselPos.y); - minY = Math.Min(minY, vesselPos.y); + bounds.Add(vesselPos.x, vesselPos.y); } else if (node.nextPatch.EndUT > 0.0) { // If the next patch is hyperbolic, include the endpoint. - vesselPos = screenTransform.MultiplyPoint3x4(vessel.orbit.SwappedRelativePositionAtUT(node.nextPatch.EndUT)); - maxX = Math.Max(maxX, vesselPos.x); - minX = Math.Min(minX, vesselPos.x); - maxY = Math.Max(maxY, vesselPos.y); - minY = Math.Min(minY, vesselPos.y); + vesselPos = screenTransform.MultiplyPoint3x4(selectedPatch.SwappedRelativePositionAtUT(node.nextPatch.EndUT)); + bounds.Add(vesselPos.x, vesselPos.y); } } // Add translation. This will ensure that all of the features // under consideration above will be displayed. - screenTransform[0, 3] = -0.5f * (float)(maxX + minX); - screenTransform[1, 3] = -0.5f * (float)(maxY + minY); + screenTransform[0, 3] = -0.5f * (float)(bounds.maxX + bounds.minX); + screenTransform[1, 3] = -0.5f * (float)(bounds.maxY + bounds.minY); - double neededWidth = maxX - minX; - double neededHeight = maxY - minY; + double neededWidth = bounds.maxX - bounds.minX; + double neededHeight = bounds.maxY - bounds.minY; // Pick a scalar that will fit the bounding box we just created. float pixelScalar = (float)Math.Min(horizPixelSize / neededWidth, vertPixelSize / neededHeight); @@ -487,62 +493,62 @@ public bool RenderOrbit(RenderTexture screen, float cameraAspect) Vector3 focusCenter = screenTransform.MultiplyPoint3x4(new Vector3(0.0f, 0.0f, 0.0f)); // orbitDriver is null on the sun, so we'll just use white instead. - GL.Color((vessel.mainBody.orbitDriver == null) ? new Color(1.0f, 1.0f, 1.0f) : vessel.mainBody.orbitDriver.orbitColor); - DrawCircle(focusCenter.x, focusCenter.y, (float)(vessel.mainBody.Radius * pixelScalar)); - if (vessel.mainBody.atmosphere) + GL.Color((selectedPatch.referenceBody.orbitDriver == null) ? new Color(1.0f, 1.0f, 1.0f) : selectedPatch.referenceBody.orbitDriver.orbitColor); + DrawCircle(focusCenter.x, focusCenter.y, (float)(selectedPatch.referenceBody.Radius * pixelScalar)); + if (selectedPatch.referenceBody.atmosphere) { // Use the atmospheric ambient to color the atmosphere circle. - GL.Color(vessel.mainBody.atmosphericAmbientColor); + GL.Color(selectedPatch.referenceBody.atmosphericAmbientColor); - DrawCircle(focusCenter.x, focusCenter.y, (float)((vessel.mainBody.Radius + vessel.mainBody.atmosphereDepth) * pixelScalar)); + DrawCircle(focusCenter.x, focusCenter.y, (float)((selectedPatch.referenceBody.Radius + selectedPatch.referenceBody.atmosphereDepth) * pixelScalar)); } if (targetVessel != null && !targetVessel.LandedOrSplashed) { GL.Color(iconColorTargetValue); - if (!targetVessel.orbit.activePatch && targetVessel.orbit.eccentricity < 1.0 && targetVessel.orbit.referenceBody == vessel.orbit.referenceBody) + if (!targetVessel.orbit.activePatch && targetVessel.orbit.eccentricity < 1.0 && targetVessel.orbit.referenceBody == selectedPatch.referenceBody) { // For some reason, activePatch is false for targetVessel. // If we have a stable orbit for the target, use a fallback // rendering method: - ReallyDrawOrbit(targetVessel.orbit, vessel.orbit.referenceBody, screenTransform); + ReallyDrawOrbit(targetVessel.orbit, selectedPatch.referenceBody, screenTransform); } else { - DrawOrbit(targetVessel.orbit, vessel.orbit.referenceBody, screenTransform); + DrawOrbit(targetVessel.orbit, selectedPatch.referenceBody, screenTransform); } } - foreach (CelestialBody moon in vessel.orbit.referenceBody.orbitingBodies) + foreach (CelestialBody moon in selectedPatch.referenceBody.orbitingBodies) { if (moon != targetBody) { GL.Color(moon.orbitDriver.orbitColor); - ReallyDrawOrbit(moon.GetOrbit(), vessel.orbit.referenceBody, screenTransform); + ReallyDrawOrbit(moon.GetOrbit(), selectedPatch.referenceBody, screenTransform); } } if (targetBody != null) { GL.Color(iconColorTargetValue); - ReallyDrawOrbit(targetBody.GetOrbit(), vessel.orbit.referenceBody, screenTransform); + ReallyDrawOrbit(targetBody.GetOrbit(), selectedPatch.referenceBody, screenTransform); } if (node != null) { GL.Color(orbitColorNextNodeValue); - DrawOrbit(node.nextPatch, vessel.orbit.referenceBody, screenTransform); + DrawOrbit(node.nextPatch, selectedPatch.referenceBody, screenTransform); } - if (vessel.orbit.nextPatch != null && vessel.orbit.nextPatch.activePatch) + if (selectedPatch.nextPatch != null && selectedPatch.nextPatch.activePatch) { GL.Color(orbitColorNextNodeValue); - DrawOrbit(vessel.orbit.nextPatch, vessel.orbit.referenceBody, screenTransform); + DrawOrbit(selectedPatch.nextPatch, selectedPatch.referenceBody, screenTransform); } // Draw the vessel orbit GL.Color(orbitColorSelfValue); - DrawOrbit(vessel.orbit, vessel.orbit.referenceBody, screenTransform); + DrawOrbit(selectedPatch, selectedPatch.referenceBody, screenTransform); // Done drawing lines. Reset color to white, so we don't mess up anyone else. GL.Color(Color.white); @@ -550,11 +556,11 @@ public bool RenderOrbit(RenderTexture screen, float cameraAspect) // Draw target vessel icons. Vector3 transformedPosition; - foreach (CelestialBody moon in vessel.orbit.referenceBody.orbitingBodies) + foreach (CelestialBody moon in selectedPatch.referenceBody.orbitingBodies) { if (moon != targetBody) { - transformedPosition = screenTransform.MultiplyPoint3x4(moon.getTruePositionAtUT(now) - vessel.orbit.referenceBody.getTruePositionAtUT(now)); + transformedPosition = screenTransform.MultiplyPoint3x4(moon.getTruePositionAtUT(now) - selectedPatch.referenceBody.getTruePositionAtUT(now)); DrawIcon(transformedPosition.x, transformedPosition.y, VesselType.Unknown, moon.orbitDriver.orbitColor, MapIcons.OtherIcon.PLANET); } } @@ -567,19 +573,19 @@ public bool RenderOrbit(RenderTexture screen, float cameraAspect) if (targetVessel != null && targetVessel.LandedOrSplashed) { - orbit = JUtil.ClosestApproachSrfOrbit(vessel.orbit, targetVessel, out tClosestApproach, out dClosestApproach); + orbit = JUtil.ClosestApproachSrfOrbit(selectedPatch, targetVessel, out tClosestApproach, out dClosestApproach); } else { - dClosestApproach = JUtil.GetClosestApproach(vessel.orbit, orbit, out tClosestApproach); + dClosestApproach = JUtil.GetClosestApproach(selectedPatch, orbit, out tClosestApproach); - DrawNextPe(orbit, vessel.orbit.referenceBody, now, iconColorTargetValue, screenTransform); - DrawNextAp(orbit, vessel.orbit.referenceBody, now, iconColorTargetValue, screenTransform); + DrawNextPe(orbit, selectedPatch.referenceBody, now, iconColorTargetValue, screenTransform); + DrawNextAp(orbit, selectedPatch.referenceBody, now, iconColorTargetValue, screenTransform); } if (targetBody != null) { - transformedPosition = screenTransform.MultiplyPoint3x4(targetBody.getTruePositionAtUT(now) - vessel.orbit.referenceBody.getTruePositionAtUT(now)); + transformedPosition = screenTransform.MultiplyPoint3x4(targetBody.getTruePositionAtUT(now) - selectedPatch.referenceBody.getTruePositionAtUT(now)); DrawIcon(transformedPosition.x, transformedPosition.y, VesselType.Unknown, iconColorTargetValue, MapIcons.OtherIcon.PLANET); } else @@ -588,29 +594,29 @@ public bool RenderOrbit(RenderTexture screen, float cameraAspect) DrawIcon(transformedPosition.x, transformedPosition.y, targetVessel.vesselType, iconColorTargetValue); } - if (vessel.orbit.AscendingNodeExists(orbit)) + if (selectedPatch.AscendingNodeExists(orbit)) { - double anTime = vessel.orbit.TimeOfAscendingNode(orbit, now); - if (anTime < vessel.orbit.EndUT || (vessel.orbit.patchEndTransition != Orbit.PatchTransitionType.ESCAPE && vessel.orbit.patchEndTransition != Orbit.PatchTransitionType.ENCOUNTER)) + double anTime = selectedPatch.TimeOfAscendingNode(orbit, now); + if (anTime < selectedPatch.EndUT || (selectedPatch.patchEndTransition != Orbit.PatchTransitionType.ESCAPE && selectedPatch.patchEndTransition != Orbit.PatchTransitionType.ENCOUNTER)) { - transformedPosition = screenTransform.MultiplyPoint3x4(vessel.orbit.SwappedRelativePositionAtUT(anTime)); + transformedPosition = screenTransform.MultiplyPoint3x4(selectedPatch.SwappedRelativePositionAtUT(anTime)); DrawIcon(transformedPosition.x, transformedPosition.y, VesselType.Unknown, orbitColorSelfValue, MapIcons.OtherIcon.AN); } } - if (vessel.orbit.DescendingNodeExists(orbit)) + if (selectedPatch.DescendingNodeExists(orbit)) { - double dnTime = vessel.orbit.TimeOfDescendingNode(orbit, now); - if (dnTime < vessel.orbit.EndUT || (vessel.orbit.patchEndTransition != Orbit.PatchTransitionType.ESCAPE && vessel.orbit.patchEndTransition != Orbit.PatchTransitionType.ENCOUNTER)) + double dnTime = selectedPatch.TimeOfDescendingNode(orbit, now); + if (dnTime < selectedPatch.EndUT || (selectedPatch.patchEndTransition != Orbit.PatchTransitionType.ESCAPE && selectedPatch.patchEndTransition != Orbit.PatchTransitionType.ENCOUNTER)) { - transformedPosition = screenTransform.MultiplyPoint3x4(vessel.orbit.SwappedRelativePositionAtUT(dnTime)); + transformedPosition = screenTransform.MultiplyPoint3x4(selectedPatch.SwappedRelativePositionAtUT(dnTime)); DrawIcon(transformedPosition.x, transformedPosition.y, VesselType.Unknown, orbitColorSelfValue, MapIcons.OtherIcon.DN); } } - Orbit o = GetPatchAtUT(vessel.orbit, tClosestApproach); + Orbit o = GetPatchAtUT(selectedPatch, tClosestApproach); if (o != null) { - Vector3d encounterPosition = o.SwappedRelativePositionAtUT(tClosestApproach) + o.referenceBody.getTruePositionAtUT(tClosestApproach) - vessel.orbit.referenceBody.getTruePositionAtUT(tClosestApproach); + Vector3d encounterPosition = o.SwappedRelativePositionAtUT(tClosestApproach) + o.referenceBody.getTruePositionAtUT(tClosestApproach) - selectedPatch.referenceBody.getTruePositionAtUT(tClosestApproach); transformedPosition = screenTransform.MultiplyPoint3x4(encounterPosition); DrawIcon(transformedPosition.x, transformedPosition.y, VesselType.Unknown, iconColorClosestApproachValue, MapIcons.OtherIcon.SHIPATINTERCEPT); } @@ -622,57 +628,66 @@ public bool RenderOrbit(RenderTexture screen, float cameraAspect) } else { - if (vessel.orbit.AscendingNodeEquatorialExists()) + if (selectedPatch.AscendingNodeEquatorialExists()) { - double anTime = vessel.orbit.TimeOfAscendingNodeEquatorial(now); - if (anTime < vessel.orbit.EndUT || (vessel.orbit.patchEndTransition != Orbit.PatchTransitionType.ESCAPE && vessel.orbit.patchEndTransition != Orbit.PatchTransitionType.ENCOUNTER)) + double anTime = selectedPatch.TimeOfAscendingNodeEquatorial(now); + if (anTime < selectedPatch.EndUT || (selectedPatch.patchEndTransition != Orbit.PatchTransitionType.ESCAPE && selectedPatch.patchEndTransition != Orbit.PatchTransitionType.ENCOUNTER)) { - transformedPosition = screenTransform.MultiplyPoint3x4(vessel.orbit.SwappedRelativePositionAtUT(anTime)); + transformedPosition = screenTransform.MultiplyPoint3x4(selectedPatch.SwappedRelativePositionAtUT(anTime)); DrawIcon(transformedPosition.x, transformedPosition.y, VesselType.Unknown, orbitColorSelfValue, MapIcons.OtherIcon.AN); } } - if (vessel.orbit.DescendingNodeEquatorialExists()) + if (selectedPatch.DescendingNodeEquatorialExists()) { - double dnTime = vessel.orbit.TimeOfDescendingNodeEquatorial(now); - if (dnTime < vessel.orbit.EndUT || (vessel.orbit.patchEndTransition != Orbit.PatchTransitionType.ESCAPE && vessel.orbit.patchEndTransition != Orbit.PatchTransitionType.ENCOUNTER)) + double dnTime = selectedPatch.TimeOfDescendingNodeEquatorial(now); + if (dnTime < selectedPatch.EndUT || (selectedPatch.patchEndTransition != Orbit.PatchTransitionType.ESCAPE && selectedPatch.patchEndTransition != Orbit.PatchTransitionType.ENCOUNTER)) { - transformedPosition = screenTransform.MultiplyPoint3x4(vessel.orbit.SwappedRelativePositionAtUT(dnTime)); + transformedPosition = screenTransform.MultiplyPoint3x4(selectedPatch.SwappedRelativePositionAtUT(dnTime)); DrawIcon(transformedPosition.x, transformedPosition.y, VesselType.Unknown, orbitColorSelfValue, MapIcons.OtherIcon.DN); } } } // Draw orbital features - DrawNextPe(vessel.orbit, vessel.orbit.referenceBody, now, iconColorPEValue, screenTransform); + DrawNextPe(selectedPatch, selectedPatch.referenceBody, now, iconColorPEValue, screenTransform); - DrawNextAp(vessel.orbit, vessel.orbit.referenceBody, now, iconColorAPValue, screenTransform); + DrawNextAp(selectedPatch, selectedPatch.referenceBody, now, iconColorAPValue, screenTransform); - if (vessel.orbit.nextPatch != null && vessel.orbit.nextPatch.activePatch) + if (selectedPatch.nextPatch != null && selectedPatch.nextPatch.activePatch) { - transformedPosition = screenTransform.MultiplyPoint3x4(vessel.orbit.SwappedRelativePositionAtUT(vessel.orbit.EndUT)); + transformedPosition = screenTransform.MultiplyPoint3x4(selectedPatch.SwappedRelativePositionAtUT(selectedPatch.EndUT)); DrawIcon(transformedPosition.x, transformedPosition.y, VesselType.Unknown, orbitColorSelfValue, MapIcons.OtherIcon.EXITSOI); - Orbit nextPatch = vessel.orbit.nextPatch.nextPatch; + Orbit nextPatch = selectedPatch.nextPatch.nextPatch; if (nextPatch != null && nextPatch.activePatch) { - transformedPosition = screenTransform.MultiplyPoint3x4(nextPatch.SwappedRelativePositionAtUT(nextPatch.EndUT) + nextPatch.referenceBody.getTruePositionAtUT(nextPatch.EndUT) - vessel.orbit.referenceBody.getTruePositionAtUT(nextPatch.EndUT)); + transformedPosition = screenTransform.MultiplyPoint3x4(nextPatch.SwappedRelativePositionAtUT(nextPatch.EndUT) + nextPatch.referenceBody.getTruePositionAtUT(nextPatch.EndUT) - selectedPatch.referenceBody.getTruePositionAtUT(nextPatch.EndUT)); DrawIcon(transformedPosition.x, transformedPosition.y, VesselType.Unknown, orbitColorNextNodeValue, MapIcons.OtherIcon.EXITSOI); } } - if (node != null && node.nextPatch.activePatch) + if (patchIndex > 0) { - DrawNextPe(node.nextPatch, vessel.orbit.referenceBody, now, orbitColorNextNodeValue, screenTransform); + transformedPosition = screenTransform.MultiplyPoint3x4(selectedPatch.SwappedRelativePositionAtUT(selectedPatch.EndUT)); + DrawIcon(transformedPosition.x, transformedPosition.y, VesselType.Unknown, orbitColorSelfValue, MapIcons.OtherIcon.ENTERSOI); + } - DrawNextAp(node.nextPatch, vessel.orbit.referenceBody, now, orbitColorNextNodeValue, screenTransform); + if (node != null && node.nextPatch.activePatch) + { + DrawNextPe(node.nextPatch, selectedPatch.referenceBody, now, orbitColorNextNodeValue, screenTransform); - transformedPosition = screenTransform.MultiplyPoint3x4(vessel.orbit.SwappedRelativePositionAtUT(node.UT)); - DrawIcon(transformedPosition.x, transformedPosition.y, VesselType.Unknown, orbitColorNextNodeValue, MapIcons.OtherIcon.NODE); - } + DrawNextAp(node.nextPatch, selectedPatch.referenceBody, now, orbitColorNextNodeValue, screenTransform); + + transformedPosition = screenTransform.MultiplyPoint3x4(selectedPatch.SwappedRelativePositionAtUT(node.UT)); + DrawIcon(transformedPosition.x, transformedPosition.y, VesselType.Unknown, orbitColorNextNodeValue, MapIcons.OtherIcon.NODE); + } // Draw ownship icon - transformedPosition = screenTransform.MultiplyPoint3x4(vessel.orbit.SwappedRelativePositionAtUT(now)); - DrawIcon(transformedPosition.x, transformedPosition.y, vessel.vesselType, iconColorSelfValue); + if (selectedPatch.referenceBody == vessel.orbit.referenceBody) + { + transformedPosition = screenTransform.MultiplyPoint3x4(vessel.orbit.SwappedRelativePositionAtUT(now)); + DrawIcon(transformedPosition.x, transformedPosition.y, vessel.vesselType, iconColorSelfValue); + } GL.PopMatrix(); GL.Viewport(new Rect(0, 0, screen.width, screen.height)); @@ -802,5 +817,27 @@ public void Start() throw; } } + + private class Bounds + { + public double maxX; + public double minX; + public double maxY; + public double minY; + + public Bounds(double radius) + { + Add(radius, radius); + Add(-radius, -radius); + } + + public void Add(double x, double y) + { + maxX = Math.Max(maxX, x); + minX = Math.Min(minX, x); + maxY = Math.Max(maxY, y); + minY = Math.Min(minY, y); + } + } } }