From cccf21d6958094518824ea412276823cbe0ffb2b Mon Sep 17 00:00:00 2001 From: Andrew Pritchard Date: Mon, 29 Sep 2025 16:28:56 +1000 Subject: [PATCH 01/12] Allow Selecting orbital patches in IVA --- .../Auxiliary modules/JSILabel.cs | 6 +- .../JSISwitchableVariableLabel.cs | 4 +- .../Auxiliary modules/JSIVariableLabel.cs | 2 +- RasterPropMonitor/Core/GenericVariable.cs | 29 ++ RasterPropMonitor/Core/IPageElement.cs | 7 + RasterPropMonitor/Core/IVariable.cs | 13 + RasterPropMonitor/Core/MonitorPage.cs | 29 +- RasterPropMonitor/Core/OrbitExtensions.cs | 27 +- RasterPropMonitor/Core/RPMCEvaluators.cs | 4 +- RasterPropMonitor/Core/RPMVesselComputer.cs | 6 +- RasterPropMonitor/Core/RasterPropMonitor.cs | 276 +++++++++++- .../Core/RasterPropMonitorComputer.cs | 15 +- RasterPropMonitor/Core/StringProcessor.cs | 140 +++--- RasterPropMonitor/Core/VariableOrNumber.cs | 21 +- RasterPropMonitor/Handlers/JSIOrbitDisplay.cs | 407 ++++++++++-------- .../Handlers/JSIPatchSelector.cs | 38 ++ 16 files changed, 705 insertions(+), 319 deletions(-) create mode 100644 RasterPropMonitor/Core/GenericVariable.cs create mode 100644 RasterPropMonitor/Core/IPageElement.cs create mode 100644 RasterPropMonitor/Core/IVariable.cs create mode 100644 RasterPropMonitor/Handlers/JSIPatchSelector.cs diff --git a/RasterPropMonitor/Auxiliary modules/JSILabel.cs b/RasterPropMonitor/Auxiliary modules/JSILabel.cs index e3c14135..511b8602 100644 --- a/RasterPropMonitor/Auxiliary modules/JSILabel.cs +++ b/RasterPropMonitor/Auxiliary modules/JSILabel.cs @@ -230,7 +230,7 @@ public void Start() if (oneshot) { - textObj.text = labels[0].Get(); + textObj.text = labels[0].GetFormattedString(); var propBatcher = internalModel.GetComponentInChildren(); if (propBatcher != null && canBatch) @@ -338,7 +338,7 @@ public void Click() activeLabel = 0; } - textObj.text = labels[activeLabel].Get(); + textObj.text = labels[activeLabel].GetFormattedString(); // do we need to activate the update loop? if (labels.Count > 1 && !labels[activeLabel].IsConstant) @@ -492,7 +492,7 @@ public override void OnUpdate() if (UpdateCheck() && JUtil.RasterPropMonitorShouldUpdate(part)) { - textObj.text = labels[activeLabel].Get(); + textObj.text = labels[activeLabel].GetFormattedString(); } } } diff --git a/RasterPropMonitor/Auxiliary modules/JSISwitchableVariableLabel.cs b/RasterPropMonitor/Auxiliary modules/JSISwitchableVariableLabel.cs index e8766d42..bcc4c599 100644 --- a/RasterPropMonitor/Auxiliary modules/JSISwitchableVariableLabel.cs +++ b/RasterPropMonitor/Auxiliary modules/JSISwitchableVariableLabel.cs @@ -190,7 +190,7 @@ public override void OnUpdate() { if (UpdateCheck()) { - textObj.text.text = StringProcessor.ProcessString(labelsEx[activeLabel].label, rpmComp); + textObj.text.text = labelsEx[activeLabel].label.GetFormattedString(); if (labelsEx[activeLabel].oneShot) { @@ -232,7 +232,7 @@ void UpdateActiveLabel(int direction) if (labelsEx[activeLabel].hasText) { - textObj.text.text = StringProcessor.ProcessString(labelsEx[activeLabel].label, rpmComp); + textObj.text.text = labelsEx[activeLabel].label.GetFormattedString(); } // Force an update. diff --git a/RasterPropMonitor/Auxiliary modules/JSIVariableLabel.cs b/RasterPropMonitor/Auxiliary modules/JSIVariableLabel.cs index eb82f8e4..42538aa0 100644 --- a/RasterPropMonitor/Auxiliary modules/JSIVariableLabel.cs +++ b/RasterPropMonitor/Auxiliary modules/JSIVariableLabel.cs @@ -190,7 +190,7 @@ public override void OnUpdate() if (UpdateCheck()) { - textObj.text.text = StringProcessor.ProcessString(spf, rpmComp); + textObj.text.text = spf.GetFormattedString(); oneshotComplete = true; } } diff --git a/RasterPropMonitor/Core/GenericVariable.cs b/RasterPropMonitor/Core/GenericVariable.cs new file mode 100644 index 00000000..e3774883 --- /dev/null +++ b/RasterPropMonitor/Core/GenericVariable.cs @@ -0,0 +1,29 @@ +using System; + +namespace JSI +{ + public class GenericVariable : IVariable + { + private Func Generator; + + internal GenericVariable(Func generator) + { + Generator = generator; + } + + object IVariable.GetValue() + { + return Generator(); + } + + bool IVariable.Changed(object oldValue) + { + return true; + } + + bool IVariable.IsConstant() + { + return false; + } + } +} \ No newline at end of file diff --git a/RasterPropMonitor/Core/IPageElement.cs b/RasterPropMonitor/Core/IPageElement.cs new file mode 100644 index 00000000..26cbf3a8 --- /dev/null +++ b/RasterPropMonitor/Core/IPageElement.cs @@ -0,0 +1,7 @@ +namespace JSI +{ + internal interface IPageElement + { + void HandlePageCreate(RasterPropMonitor propMonitor); + } +} diff --git a/RasterPropMonitor/Core/IVariable.cs b/RasterPropMonitor/Core/IVariable.cs new file mode 100644 index 00000000..16327830 --- /dev/null +++ b/RasterPropMonitor/Core/IVariable.cs @@ -0,0 +1,13 @@ +namespace JSI +{ + /// + /// This class exists to provide a base class that RasterPropMonitorComputer + /// manages for tracking various built-in plugin action handlers. + /// + internal interface IVariable + { + object GetValue(); + bool Changed(object oldValue); + bool IsConstant(); + } +} diff --git a/RasterPropMonitor/Core/MonitorPage.cs b/RasterPropMonitor/Core/MonitorPage.cs index 5ff06303..ed879ce6 100644 --- a/RasterPropMonitor/Core/MonitorPage.cs +++ b/RasterPropMonitor/Core/MonitorPage.cs @@ -22,7 +22,6 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; -using System.Text; using UnityEngine; namespace JSI @@ -80,7 +79,7 @@ public enum BackgroundType private readonly MonoBehaviour backgroundHandlerModule, pageHandlerModule; private readonly List techsRequired = new List(); private readonly string fallbackPageName = string.Empty; - + private readonly PatchSelector patchSelector; private struct HandlerSupportMethods { @@ -136,7 +135,7 @@ public void UpdateText(RasterPropMonitorComputer rpmComp) allTextConstant = true; for (int i = 0; i < linesArray.Length; ++i) { - spf[i] = new StringProcessorFormatter(linesArray[i], rpmComp); + spf[i] = new StringProcessorFormatter(linesArray[i], rpmComp, ourMonitor); outputLines[i] = spf[i].cachedResult; @@ -156,7 +155,7 @@ public void UpdateText(RasterPropMonitorComputer rpmComp) { if (spf[i] != null) { - outputLines[i] = StringProcessor.ProcessString(spf[i], rpmComp); + outputLines[i] = spf[i].GetFormattedString(); } } } @@ -433,6 +432,13 @@ public MonitorPage(int idNum, ConfigNode node, RasterPropMonitor thatMonitor) } } + if (node.HasNode("PATCHSELECTOR")) + { + foreach (ConfigNode patchSelectorNode in node.GetNodes("PATCHSELECTOR")) + { + patchSelector = new PatchSelector(ourMonitor, patchSelectorNode); + } + } } private static MethodInfo InstantiateHandler(ConfigNode node, RasterPropMonitor ourMonitor, out MonoBehaviour moduleInstance, out HandlerSupportMethods support) @@ -593,6 +599,11 @@ private static MethodInfo InstantiateHandler(ConfigNode node, RasterPropMonitor } } + if (thatModule is IPageElement pageElement) + { + pageElement.HandlePageCreate(ourMonitor); + } + moduleInstance = thatModule; foreach (MethodInfo m in thatModule.GetType().GetMethods()) { @@ -626,17 +637,27 @@ public bool GlobalButtonClick(int buttonID) { return false; } + bool actionTaken = false; + if (pageHandlerS.buttonClick != null) { pageHandlerS.buttonClick(buttonID); actionTaken = true; } + if (backgroundHandlerS.buttonClick != null && pageHandlerS.buttonClick != backgroundHandlerS.buttonClick) { backgroundHandlerS.buttonClick(buttonID); actionTaken = true; } + + if (patchSelector != null) + { + patchSelector.HandleButtonPress(buttonID); + 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..c3e687ac 100644 --- a/RasterPropMonitor/Core/RPMCEvaluators.cs +++ b/RasterPropMonitor/Core/RPMCEvaluators.cs @@ -1601,6 +1601,8 @@ internal NumericVariableEvaluator GetNumericEvaluator(string input, out Variable return double.NaN; }; case "TARGETEXISTS": + return (RPMVesselComputer comp) => comp.target == null ? -1d : 1d; + case "TARGETISVESSEL": return (RPMVesselComputer comp) => { if (comp.target == null) @@ -1673,7 +1675,7 @@ internal NumericVariableEvaluator GetNumericEvaluator(string input, out Variable if (comp.target is Vessel || comp.target is ModuleDockingNode) { return comp.target.GetVessel().mainBody.GetAltitude(comp.target.GetVessel().CoM); - } + } else { return vessel.mainBody.GetAltitude(comp.target.GetTransform().position); diff --git a/RasterPropMonitor/Core/RPMVesselComputer.cs b/RasterPropMonitor/Core/RPMVesselComputer.cs index fba8d95a..1e145bc5 100644 --- a/RasterPropMonitor/Core/RPMVesselComputer.cs +++ b/RasterPropMonitor/Core/RPMVesselComputer.cs @@ -25,8 +25,6 @@ using System.Diagnostics; using System.Reflection; using UnityEngine; -using KSP.UI.Screens.Flight; -using static JSI.RasterPropMonitorComputer; // MOARdV TODO: // Add callbacks for docking, undocking, staging, vessel switching @@ -41,6 +39,10 @@ // ? GameEvents.onKerbalRemoved namespace JSI { + /// + /// The computer for the vessel. This class can be used for shared data + /// across different pods in the same vessel. + /// public partial class RPMVesselComputer : VesselModule { #region Static Variables diff --git a/RasterPropMonitor/Core/RasterPropMonitor.cs b/RasterPropMonitor/Core/RasterPropMonitor.cs index 3fc12eb1..649374f6 100644 --- a/RasterPropMonitor/Core/RasterPropMonitor.cs +++ b/RasterPropMonitor/Core/RasterPropMonitor.cs @@ -27,6 +27,12 @@ namespace JSI { + /// + /// A class that represents an individual Raster Prop Monitor display. + /// See also RasterPropMonitorComputer which contains data for all displays + /// in a pod, and RPMVesselComputer which represents all displays in a + /// vessel + /// public class RasterPropMonitor : InternalModule { [SerializeReference] ConfigNodeHolder moduleConfig; @@ -71,11 +77,10 @@ public class RasterPropMonitor : InternalModule [KSPField] public string resourceName = "SYSR_ELECTRICCHARGE"; private bool resourceDepleted = false; // Managed by rpmComp callback - private Action delResourceCallback; [KSPField] public bool needsCommConnection = false; private bool noCommConnection = false; // Managed by rpmComp callback - private Action delCommConnectionCallback; + [KSPField] public string defaultFontTint = string.Empty; public Color defaultFontTintValue = Color.white; @@ -107,6 +112,7 @@ public class RasterPropMonitor : InternalModule private bool startupComplete; private string fontDefinitionString = @" !""#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~Δ☊¡¢£¤¥¦§¨©ª«¬☋®¯°±²³´µ¶·¸¹º»¼½¾¿"; private RasterPropMonitorComputer rpmComp; + private int selectedPatchIndex; private static Texture2D LoadFont(object caller, InternalProp thisProp, string location) { @@ -273,14 +279,12 @@ public void Start() if (needsElectricCharge) { - delResourceCallback = (Action)Delegate.CreateDelegate(typeof(Action), this, "ResourceDepletedCallback"); - rpmComp.RegisterResourceCallback(resourceName, delResourceCallback); + rpmComp.RegisterResourceCallback(resourceName, ResourceDepletedCallback); } if (needsCommConnection) { - delCommConnectionCallback = (Action)Delegate.CreateDelegate(typeof(Action), this, "CommConnectionCallback"); - rpmComp.RegisterVariableCallback("COMMNETVESSELCONTROLSTATE", delCommConnectionCallback); + rpmComp.RegisterVariableCallback("COMMNETVESSELCONTROLSTATE", CommConnectionCallback); } // And if the try block never completed, startupComplete will never be true. @@ -314,14 +318,246 @@ public void OnDestroy() { Destroy(screenMat); } - if (delResourceCallback != null) + rpmComp.UnregisterResourceCallback(resourceName, ResourceDepletedCallback); + rpmComp.UnregisterVariableCallback("COMMNETVESSELCONTROLSTATE", CommConnectionCallback); + } + + /// + /// 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); + } + + internal void SelectPatch(int patchIndex) + { + (int effectivePatchIndex, _) = EffectivePatch(patchIndex); + selectedPatchIndex = effectivePatchIndex; + } + + internal IVariable GetVariable(string variableName) + { + if (variableName == "MONITOR_LOCAL_PATCH_INDEX") + { + return new GenericVariable(() => + { + (int index, Orbit _) = GetSelectedPatch(); + return index + 1; + }); + } + if (variableName == "MONITOR_LOCAL_PATCH_COUNT") + { + return new GenericVariable(() => + { + (int index, Orbit _) = GetLastPatch(); + return index + 1; + }); + } + if (variableName == "MONITOR_LOCAL_PATCH_ALTITUDE") { - rpmComp.UnregisterResourceCallback(resourceName, delResourceCallback); + return new GenericVariable(() => GetSelectedPatchOrbit().referenceBody.GetAltitude(vessel.CoM)); } - if (delCommConnectionCallback != null) + if (variableName == "MONITOR_LOCAL_PATCH_ORBTSPEED") { - rpmComp.UnregisterVariableCallback("COMMNETVESSELCONTROLSTATE", delCommConnectionCallback); + return new GenericVariable(() => GetSelectedPatchOrbit().GetVel().magnitude); } + if (variableName == "MONITOR_LOCAL_PATCH_APOAPSIS") + { + return new GenericVariable(() => JUtil.OrbitMakesSense(vessel) ? GetSelectedPatchOrbit().ApA : double.NaN); + } + if (variableName == "MONITOR_LOCAL_PATCH_PERIAPSIS") + { + return new GenericVariable(() => JUtil.OrbitMakesSense(vessel) ? GetSelectedPatchOrbit().PeA : double.NaN); + } + if (variableName == "MONITOR_LOCAL_PATCH_INCLINATION") + { + return new GenericVariable(() => JUtil.OrbitMakesSense(vessel) ? GetSelectedPatchOrbit().inclination : double.NaN); + } + if (variableName == "MONITOR_LOCAL_PATCH_ECCENTRICITY") + { + return new GenericVariable(() => JUtil.OrbitMakesSense(vessel) ? GetSelectedPatchOrbit().eccentricity : double.NaN); + } + if (variableName == "MONITOR_LOCAL_PATCH_TIMETOAPSECS") + { + return new GenericVariable(() => + { + 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(); + }); + } + if (variableName == "MONITOR_LOCAL_PATCH_TIMETOPESECS") + { + return new GenericVariable(() => + { + 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(); + }); + } + if (variableName == "MONITOR_LOCAL_PATCH_ORBPERIODSECS") + { + return new GenericVariable(() => JUtil.OrbitMakesSense(vessel) ? GetSelectedPatchOrbit().period : double.NaN); + } + if (variableName == "MONITOR_LOCAL_PATCH_ORBITBODY") + { + return new GenericVariable(() => GetSelectedPatchOrbit().referenceBody.name); + } + if (variableName == "MONITOR_LOCAL_PATCH_TIMETOANEQUATORIAL") + { + return new GenericVariable(() => + { + Orbit patch = GetSelectedPatchOrbit(); + if (!JUtil.OrbitMakesSense(vessel) || !patch.AscendingNodeEquatorialExists()) + { + return double.NaN; + } + return patch.TimeOfAscendingNodeEquatorial(Planetarium.GetUniversalTime()) - Planetarium.GetUniversalTime(); + }); + } + if (variableName == "MONITOR_LOCAL_PATCH_TIMETODNEQUATORIAL") + { + return new GenericVariable(() => + { + Orbit patch = GetSelectedPatchOrbit(); + if (!JUtil.OrbitMakesSense(vessel) || !patch.DescendingNodeEquatorialExists()) + { + return double.NaN; + } + return patch.TimeOfDescendingNodeEquatorial(Planetarium.GetUniversalTime()) - Planetarium.GetUniversalTime(); + }); + } + if (variableName == "MONITOR_LOCAL_PATCH_FIRST") + { + return new GenericVariable(() => + { + (int index, Orbit _) = GetSelectedPatch(); + return index == 0 ? 1.0d : 0.0d; + }); + } + if (variableName == "MONITOR_LOCAL_PATCH_LAST") + { + return new GenericVariable(() => + { + (int selected, Orbit _) = GetSelectedPatch(); + (int last, Orbit _) = GetLastPatch(); + return selected == last ? 1.0d : 0.0d; + }); + } + if (variableName == "MONITOR_LOCAL_PATCH_NEXTAPSISTYPE") + { + return new GenericVariable(() => + { + 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; + }); + } + if (variableName == "MONITOR_LOCAL_PATCH_NEXT_ANDN_EQUATORIAL") + { + return new GenericVariable(() => + { + 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; + }); + } + if (variableName == "MONITOR_LOCAL_PATCH_ENCOUNTEREXISTS") + { + return new GenericVariable(() => + { + 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; + } + }); + } + if (variableName == "MONITOR_LOCAL_PATCH_ENCOUNTERTIME") + { + return new GenericVariable(() => + { + 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; + }); + } + if (variableName == "MONITOR_LOCAL_PATCH_ENCOUNTERBODY") + { + return new GenericVariable(() => + { + if (!JUtil.OrbitMakesSense(vessel)) return 0.0; + 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; + }); + } + + return null; } private static void PlayClickSound(FXGroup audioOutput) @@ -610,6 +846,26 @@ void CommConnectionCallback(float newValue) noCommConnection = false; } } + + /// + /// 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); + } } } diff --git a/RasterPropMonitor/Core/RasterPropMonitorComputer.cs b/RasterPropMonitor/Core/RasterPropMonitorComputer.cs index 15a9a8b3..d930ad20 100644 --- a/RasterPropMonitor/Core/RasterPropMonitorComputer.cs +++ b/RasterPropMonitor/Core/RasterPropMonitorComputer.cs @@ -22,8 +22,6 @@ using System.Collections.Generic; using System.Text.RegularExpressions; using UnityEngine; -using UnityEngine.Diagnostics; -using UnityEngine.Profiling; namespace JSI { @@ -32,6 +30,11 @@ namespace JSI // onCrewBoardVessel // onCrewOnEva // onCrewTransferred + + /// + /// The computer for the pod. This class can be used for shared data across + /// different screens in the same pod. + /// public partial class RasterPropMonitorComputer : PartModule { // The only public configuration variable. @@ -94,7 +97,6 @@ internal PeriodicRandomValue(int period_) // Data refresh private int dataUpdateCountdown; private int refreshDataRate = 60; - private bool timeToUpdate = false; // Diagnostics private int debug_fixedUpdates = 0; @@ -310,10 +312,6 @@ private void ClearVariables() { sideSlipEvaluator = null; angleOfAttackEvaluator = null; - - //forceCallbackRefresh = true; - //variableCache.Clear(); - timeToUpdate = true; } // provide a way for internal modules to remove themselves temporarily from InternalProps @@ -528,8 +526,6 @@ void UpdateVariables() ++debug_fixedUpdates; - timeToUpdate = false; - Vessel v = vessel; for (int i = 0; i < activeTriggeredEvents.Count; ++i) { @@ -563,7 +559,6 @@ public void Update() if (--dataUpdateCountdown < 0) { dataUpdateCountdown = refreshDataRate; - timeToUpdate = true; UpdateVariables(); } } diff --git a/RasterPropMonitor/Core/StringProcessor.cs b/RasterPropMonitor/Core/StringProcessor.cs index 3265924f..3f2fa996 100644 --- a/RasterPropMonitor/Core/StringProcessor.cs +++ b/RasterPropMonitor/Core/StringProcessor.cs @@ -19,16 +19,13 @@ * along with RasterPropMonitor. If not, see . ****************************************************************************/ using System; -using UnityEngine.Profiling; +using System.Diagnostics; namespace JSI { public class StringProcessorFormatter { - // The formatString or plain text (if usesComp is false). - private readonly string formatString; - // An array of source variables - public readonly VariableOrNumber[] sourceVariables; + internal static readonly SIFormatProvider fp = new SIFormatProvider(); // An array holding evaluants public readonly object[] sourceValues; @@ -36,107 +33,94 @@ public class StringProcessorFormatter public string cachedResult; - // TODO: Add support for multi-line processed support. - public StringProcessorFormatter(string input, RasterPropMonitorComputer rpmComp) + // An array of source variables + internal readonly IVariable[] sourceVariables; + // The formatString or plain text (if usesComp is false). + private readonly string formatString; + + public StringProcessorFormatter(string input, RasterPropMonitorComputer rpmComp, RasterPropMonitor rpm = null) { - if(string.IsNullOrEmpty(input)) + if (string.IsNullOrEmpty(input)) { cachedResult = ""; + return; } - else if (input.IndexOf(JUtil.VariableListSeparator[0], StringComparison.Ordinal) >= 0) + + if (input.IndexOf(JUtil.VariableListSeparator[0], StringComparison.Ordinal) < 0) { - string[] tokens = input.Split(JUtil.VariableListSeparator, StringSplitOptions.RemoveEmptyEntries); - if (tokens.Length != 2) + cachedResult = input.TrimEnd(); + return; + } + + string[] tokens = input.Split(JUtil.VariableListSeparator, StringSplitOptions.RemoveEmptyEntries); + if (tokens.Length != 2) + { + throw new ArgumentException(string.Format("Invalid format string: {0}", input)); + } + + bool allVariablesConstant = true; + + string[] sourceVarStrings = tokens[1].Split(JUtil.VariableSeparator, StringSplitOptions.RemoveEmptyEntries); + sourceVariables = new IVariable[sourceVarStrings.Length]; + for (int i = 0; i < sourceVarStrings.Length; ++i) + { + var variableName = sourceVarStrings[i]; + if (variableName.StartsWith("MONITOR_LOCAL_")) { - throw new ArgumentException(string.Format("Invalid format string: {0}", input)); + sourceVariables[i] = rpm.GetVariable(variableName); } else { - bool allVariablesConstant = true; - - string[] sourceVarStrings = tokens[1].Split(JUtil.VariableSeparator, StringSplitOptions.RemoveEmptyEntries); - sourceVariables = new VariableOrNumber[sourceVarStrings.Length]; - for (int i = 0; i < sourceVarStrings.Length; ++i ) - { - sourceVariables[i] = rpmComp.InstantiateVariableOrNumber(sourceVarStrings[i]); - allVariablesConstant = allVariablesConstant && sourceVariables[i].isConstant; - } - sourceValues = new object[sourceVariables.Length]; - formatString = tokens[0].TrimEnd(); - - for (int i = 0; i < sourceVariables.Length; ++i) - { - sourceValues[i] = sourceVariables[i].Get(); - } - - cachedResult = string.Format(StringProcessor.fp, formatString, sourceValues); - - // if every variable is a constant, we can run the format once and cache the result - if (allVariablesConstant) - { - sourceVariables = null; - sourceValues = null; - } + sourceVariables[i] = rpmComp.InstantiateVariableOrNumber(sourceVarStrings[i]); } + + allVariablesConstant = allVariablesConstant && sourceVariables[i].IsConstant(); } - else - { - cachedResult = input.TrimEnd(); - } - } - public bool UpdateValues() - { - if (sourceValues == null) return false; + sourceValues = new object[sourceVariables.Length]; + formatString = tokens[0].TrimEnd(); - bool anyChanged = false; for (int i = 0; i < sourceVariables.Length; ++i) { - var sourceVariable = sourceVariables[i]; - if (!sourceVariable.isConstant) - { - if (sourceVariable.isNumeric) - { - double newValue = sourceVariable.numericValue; - if (JUtil.ValueChanged((double)sourceValues[i], newValue)) - { - anyChanged = true; - sourceValues[i] = newValue; - } - } - else - { - string newValue = sourceVariable.stringValue; - anyChanged = anyChanged || newValue != (string)sourceValues[i]; - sourceValues[i] = newValue; - } - } + sourceValues[i] = sourceVariables[i].GetValue(); } - return anyChanged; + cachedResult = string.Format(fp, formatString, sourceValues); + + // if every variable is a constant, we can run the format once and cache the result + if (allVariablesConstant) + { + sourceVariables = null; + sourceValues = null; + } } - public string Get() + public string GetFormattedString() { if (UpdateValues()) { - cachedResult = string.Format(StringProcessor.fp, formatString, sourceValues); + cachedResult = string.Format(fp, formatString, sourceValues); } return cachedResult; } - } + + private bool UpdateValues() + { + if (sourceValues == null) return false; - public static class StringProcessor - { - internal static readonly SIFormatProvider fp = new SIFormatProvider(); + bool anyChanged = false; + for (int i = 0; i < sourceVariables.Length; ++i) + { + var sourceVariable = sourceVariables[i]; + if (!sourceVariable.IsConstant()) + { + anyChanged = anyChanged || sourceVariable.Changed(sourceValues[i]); + sourceValues[i] = sourceVariable.GetValue(); + } + } - public static string ProcessString(StringProcessorFormatter formatter, RasterPropMonitorComputer rpmComp) - { - Profiler.BeginSample("ProcessString_cached"); - string result = formatter.Get(); - Profiler.EndSample(); - return result; + return anyChanged; } } } diff --git a/RasterPropMonitor/Core/VariableOrNumber.cs b/RasterPropMonitor/Core/VariableOrNumber.cs index 1dc4f288..3d998236 100644 --- a/RasterPropMonitor/Core/VariableOrNumber.cs +++ b/RasterPropMonitor/Core/VariableOrNumber.cs @@ -38,7 +38,7 @@ enum VariableUpdateType /// It's owned by the VariableCache instances inside the RasterPropMonitorComputer /// This architecture is kind of strange; this could probably be unified with VariableCache /// - public class VariableOrNumber + public class VariableOrNumber : IVariable { internal readonly string variableName; internal double numericValue; @@ -220,7 +220,7 @@ public int AsInt() /// Return the value boxed as an object /// /// - public object Get() + object IVariable.GetValue() { if (updateType == VariableUpdateType.Volatile) { @@ -240,6 +240,23 @@ public object Get() return stringValue; } } + + bool IVariable.Changed(object oldValue) + { + if (isNumeric) + { + return JUtil.ValueChanged((double)oldValue, numericValue); + } + else + { + return stringValue != (string)oldValue; + } + } + + bool IVariable.IsConstant() + { + return isConstant; + } } /// diff --git a/RasterPropMonitor/Handlers/JSIOrbitDisplay.cs b/RasterPropMonitor/Handlers/JSIOrbitDisplay.cs index dbda4b08..a691589d 100644 --- a/RasterPropMonitor/Handlers/JSIOrbitDisplay.cs +++ b/RasterPropMonitor/Handlers/JSIOrbitDisplay.cs @@ -22,14 +22,13 @@ // JSIOrbitDisplay: Display a schematic (line-art) drawing of the vessel's // orbit, marking highlights (Pe, Ap, AN, DN), along with the mainbody's // surface and atmosphere (if applicable). -using System; +using System; using UnityEngine; namespace JSI { - public class JSIOrbitDisplay : InternalModule + public class JSIOrbitDisplay : InternalModule, IPageElement { - [KSPField] public string backgroundColor = string.Empty; private Color backgroundColorValue = Color.black; [KSPField] @@ -64,20 +63,26 @@ public class JSIOrbitDisplay : InternalModule public Vector2 iconShadowShift = new Vector2(1, 1); private bool startupComplete; - private Material lineMaterial; + private Material lineMaterial; + private RasterPropMonitor rpm; - static readonly int CIRCLE_POINTS = 60; - static readonly int ORBIT_POINTS = 60; + private static readonly int CIRCLE_POINTS = 60; + private static readonly int ORBIT_POINTS = 60; - public override void OnAwake() - { - base.OnAwake(); + public override void OnAwake() + { + base.OnAwake(); - if (lineMaterial == null) - { - lineMaterial = JUtil.DrawLineMaterial(); - } - } + if (lineMaterial == null) + { + lineMaterial = JUtil.DrawLineMaterial(); + } + } + + void IPageElement.HandlePageCreate(RasterPropMonitor rpm) + { + this.rpm = rpm; + } // TODO: this could all be improved by implementint adaptive screen-space tesselation: // http://blog.johannesmp.com/2022/06/30/KSP2-Dev-Diary_Orbit-Tessellation/ @@ -129,12 +134,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,23 +162,49 @@ 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 // active. I've encountered this when targeting a vessel or planet. private static void ReallyDrawOrbit(Orbit o, CelestialBody referenceBody, Matrix4x4 screenTransform) @@ -296,6 +327,8 @@ public bool RenderOrbit(RenderTexture screen, float cameraAspect) double horizPixelSize = displayPosition.z - iconPixelSize; double vertPixelSize = displayPosition.w - iconPixelSize; + (int patchIndex, Orbit selectedPatch) = rpm.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 +340,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 +361,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 +439,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 +458,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 +498,63 @@ 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) - { - GL.Color(orbitColorNextNodeValue); - DrawOrbit(vessel.orbit.nextPatch, vessel.orbit.referenceBody, screenTransform); - } + // TODO fix this up + // if (selectedPatch.nextPatch != null && selectedPatch.nextPatch.activePatch) + // { + // GL.Color(orbitColorNextNodeValue); + // 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 +562,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 +579,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 +600,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 +634,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); + } + + if (node != null && node.nextPatch.activePatch) + { + DrawNextPe(node.nextPatch, selectedPatch.referenceBody, now, orbitColorNextNodeValue, screenTransform); - DrawNextAp(node.nextPatch, vessel.orbit.referenceBody, now, orbitColorNextNodeValue, screenTransform); + DrawNextAp(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); - } + 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)); @@ -692,14 +713,14 @@ private void DrawIcon(float xPos, float yPos, VesselType vt, Color iconColor, Ma //MapView.OrbitIconsMaterial.color = iconColorShadowValue; //Graphics.DrawTexture(shadow, MapView.OrbitIconsMap, MapIcons.VesselTypeIcon(vt, icon), 0, 0, 0, 0, MapView.OrbitIconsMaterial); - // the old icon material wasn't working, so just use this one - // but I don't fully understand the color/blend system - // a = 1.0 is far too faint; 4.0 looks pretty good + // the old icon material wasn't working, so just use this one + // but I don't fully understand the color/blend system + // a = 1.0 is far too faint; 4.0 looks pretty good MapView.OrbitIconsMaterial.color = new Color(iconColor.r, iconColor.g, iconColor.b, 4.0f); Graphics.DrawTexture(position, MapView.OrbitIconsMap, MapIcons.VesselTypeIcon(vt, icon), 0, 0, 0, 0, MapView.OrbitIconsMaterial); - - // if the icon texture ever changes, you can use this code to dump it out for inspection - #if false + + // if the icon texture ever changes, you can use this code to dump it out for inspection +#if false var filepath = "orbiticonsmap.png"; if (!System.IO.File.Exists(filepath)) { @@ -707,28 +728,28 @@ private void DrawIcon(float xPos, float yPos, VesselType vt, Color iconColor, Ma var textureBytes = textureCopy.EncodeToPNG(); System.IO.File.WriteAllBytes(filepath, textureBytes); } - #endif +#endif } - Texture2D duplicateTexture(Texture2D source) - { - RenderTexture renderTex = RenderTexture.GetTemporary( - source.width, - source.height, - 0, - RenderTextureFormat.Default, - RenderTextureReadWrite.Linear); - - Graphics.Blit(source, renderTex); - RenderTexture previous = RenderTexture.active; - RenderTexture.active = renderTex; - Texture2D readableText = new Texture2D(source.width, source.height); - readableText.ReadPixels(new Rect(0, 0, renderTex.width, renderTex.height), 0, 0); - readableText.Apply(); - RenderTexture.active = previous; - RenderTexture.ReleaseTemporary(renderTex); - return readableText; - } + Texture2D duplicateTexture(Texture2D source) + { + RenderTexture renderTex = RenderTexture.GetTemporary( + source.width, + source.height, + 0, + RenderTextureFormat.Default, + RenderTextureReadWrite.Linear); + + Graphics.Blit(source, renderTex); + RenderTexture previous = RenderTexture.active; + RenderTexture.active = renderTex; + Texture2D readableText = new Texture2D(source.width, source.height); + readableText.ReadPixels(new Rect(0, 0, renderTex.width, renderTex.height), 0, 0); + readableText.Apply(); + RenderTexture.active = previous; + RenderTexture.ReleaseTemporary(renderTex); + return readableText; + } public void Start() { @@ -802,5 +823,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); + } + } } } diff --git a/RasterPropMonitor/Handlers/JSIPatchSelector.cs b/RasterPropMonitor/Handlers/JSIPatchSelector.cs new file mode 100644 index 00000000..e108261e --- /dev/null +++ b/RasterPropMonitor/Handlers/JSIPatchSelector.cs @@ -0,0 +1,38 @@ +namespace JSI +{ + public class PatchSelector + { + private int buttonNext = 7; + private int buttonPrev = 8; + private RasterPropMonitor rpm; + + internal PatchSelector(RasterPropMonitor rpm, ConfigNode node) + { + this.rpm = rpm; + int intValue = 0; + + if (node.TryGetValue("buttonNext", ref intValue)) + { + buttonNext = intValue; + } + + if (node.TryGetValue("buttonPrev", ref intValue)) + { + buttonPrev = intValue; + } + } + + internal void HandleButtonPress(int buttonID) + { + if (buttonID == buttonNext) + { + rpm.SelectNextPatch(); + } + + if (buttonID == buttonPrev) + { + rpm.SelectPreviousPatch(); + } + } + } +} From 21bba4dd0f330319fced9a477b13246a3b72e169 Mon Sep 17 00:00:00 2001 From: JonnyOThan Date: Mon, 29 Sep 2025 08:27:22 -0400 Subject: [PATCH 02/12] Update StringProcessor.cs --- RasterPropMonitor/Core/StringProcessor.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/RasterPropMonitor/Core/StringProcessor.cs b/RasterPropMonitor/Core/StringProcessor.cs index 4b472e01..5c9df09f 100644 --- a/RasterPropMonitor/Core/StringProcessor.cs +++ b/RasterPropMonitor/Core/StringProcessor.cs @@ -27,8 +27,6 @@ public class StringProcessorFormatter { internal static readonly SIFormatProvider fp = new SIFormatProvider(); - // The formatString or plain text (if usesComp is false). - private readonly string formatString; // An array holding evaluants public readonly object[] sourceValues; From 63b97278c0ad7a2ed4a3e392f6747e789778a525 Mon Sep 17 00:00:00 2001 From: Andrew Pritchard Date: Mon, 29 Sep 2025 22:58:48 +1000 Subject: [PATCH 03/12] Minimise diff, respond to some PR comments, will do some more updates tomorrow --- RasterPropMonitor/Core/MonitorPage.cs | 5 - RasterPropMonitor/Core/RPMCEvaluators.cs | 6 +- RasterPropMonitor/Core/RPMVesselComputer.cs | 6 +- RasterPropMonitor/Core/RasterPropMonitor.cs | 7 +- .../Core/RasterPropMonitorComputer.cs | 15 ++- RasterPropMonitor/Core/StringProcessor.cs | 111 +++++++++--------- 6 files changed, 72 insertions(+), 78 deletions(-) diff --git a/RasterPropMonitor/Core/MonitorPage.cs b/RasterPropMonitor/Core/MonitorPage.cs index ed879ce6..946afc7f 100644 --- a/RasterPropMonitor/Core/MonitorPage.cs +++ b/RasterPropMonitor/Core/MonitorPage.cs @@ -637,27 +637,22 @@ public bool GlobalButtonClick(int buttonID) { return false; } - bool actionTaken = false; - if (pageHandlerS.buttonClick != null) { pageHandlerS.buttonClick(buttonID); actionTaken = true; } - if (backgroundHandlerS.buttonClick != null && pageHandlerS.buttonClick != backgroundHandlerS.buttonClick) { backgroundHandlerS.buttonClick(buttonID); actionTaken = true; } - if (patchSelector != null) { patchSelector.HandleButtonPress(buttonID); actionTaken = true; } - return actionTaken; } diff --git a/RasterPropMonitor/Core/RPMCEvaluators.cs b/RasterPropMonitor/Core/RPMCEvaluators.cs index c3e687ac..65787db8 100644 --- a/RasterPropMonitor/Core/RPMCEvaluators.cs +++ b/RasterPropMonitor/Core/RPMCEvaluators.cs @@ -1600,9 +1600,9 @@ internal NumericVariableEvaluator GetNumericEvaluator(string input, out Variable } return double.NaN; }; - case "TARGETEXISTS": + case "ANYTARGETEXISTS": return (RPMVesselComputer comp) => comp.target == null ? -1d : 1d; - case "TARGETISVESSEL": + case "TARGETEXISTS": return (RPMVesselComputer comp) => { if (comp.target == null) @@ -1675,7 +1675,7 @@ internal NumericVariableEvaluator GetNumericEvaluator(string input, out Variable if (comp.target is Vessel || comp.target is ModuleDockingNode) { return comp.target.GetVessel().mainBody.GetAltitude(comp.target.GetVessel().CoM); - } + } else { return vessel.mainBody.GetAltitude(comp.target.GetTransform().position); diff --git a/RasterPropMonitor/Core/RPMVesselComputer.cs b/RasterPropMonitor/Core/RPMVesselComputer.cs index 6f93f1af..0c4c4aee 100644 --- a/RasterPropMonitor/Core/RPMVesselComputer.cs +++ b/RasterPropMonitor/Core/RPMVesselComputer.cs @@ -25,6 +25,8 @@ using System.Diagnostics; using System.Reflection; using UnityEngine; +using KSP.UI.Screens.Flight; +using static JSI.RasterPropMonitorComputer; // MOARdV TODO: // Add callbacks for docking, undocking, staging, vessel switching @@ -39,10 +41,6 @@ // ? GameEvents.onKerbalRemoved namespace JSI { - /// - /// The computer for the vessel. This class can be used for shared data - /// across different pods in the same vessel. - /// public partial class RPMVesselComputer : VesselModule { #region Static Variables diff --git a/RasterPropMonitor/Core/RasterPropMonitor.cs b/RasterPropMonitor/Core/RasterPropMonitor.cs index 6cf611d4..fdc9ef56 100644 --- a/RasterPropMonitor/Core/RasterPropMonitor.cs +++ b/RasterPropMonitor/Core/RasterPropMonitor.cs @@ -27,12 +27,6 @@ namespace JSI { - /// - /// A class that represents an individual Raster Prop Monitor display. - /// See also RasterPropMonitorComputer which contains data for all displays - /// in a pod, and RPMVesselComputer which represents all displays in a - /// vessel - /// public class RasterPropMonitor : InternalModule { [SerializeReference] ConfigNodeHolder moduleConfig; @@ -345,6 +339,7 @@ private Orbit GetSelectedPatchOrbit() { return EffectivePatch(1000); } + internal void SelectNextPatch() { (int effectivePatchIndex, _) = GetSelectedPatch(); diff --git a/RasterPropMonitor/Core/RasterPropMonitorComputer.cs b/RasterPropMonitor/Core/RasterPropMonitorComputer.cs index 4809167f..38bbd032 100644 --- a/RasterPropMonitor/Core/RasterPropMonitorComputer.cs +++ b/RasterPropMonitor/Core/RasterPropMonitorComputer.cs @@ -22,6 +22,8 @@ using System.Collections.Generic; using System.Text.RegularExpressions; using UnityEngine; +using UnityEngine.Diagnostics; +using UnityEngine.Profiling; namespace JSI { @@ -30,11 +32,6 @@ namespace JSI // onCrewBoardVessel // onCrewOnEva // onCrewTransferred - - /// - /// The computer for the pod. This class can be used for shared data across - /// different screens in the same pod. - /// public partial class RasterPropMonitorComputer : PartModule { // The only public configuration variable. @@ -97,6 +94,7 @@ internal PeriodicRandomValue(int period_) // Data refresh private int dataUpdateCountdown; private int refreshDataRate = 60; + private bool timeToUpdate = false; // Diagnostics private int debug_fixedUpdates = 0; @@ -312,6 +310,10 @@ private void ClearVariables() { sideSlipEvaluator = null; angleOfAttackEvaluator = null; + + //forceCallbackRefresh = true; + //variableCache.Clear(); + timeToUpdate = true; } // provide a way for internal modules to remove themselves temporarily from InternalProps @@ -519,6 +521,8 @@ void UpdateVariables() ++debug_fixedUpdates; + timeToUpdate = false; + Vessel v = vessel; for (int i = 0; i < activeTriggeredEvents.Count; ++i) { @@ -552,6 +556,7 @@ public void Update() if (--dataUpdateCountdown < 0) { dataUpdateCountdown = refreshDataRate; + timeToUpdate = true; UpdateVariables(); } } diff --git a/RasterPropMonitor/Core/StringProcessor.cs b/RasterPropMonitor/Core/StringProcessor.cs index 5c9df09f..c84035dd 100644 --- a/RasterPropMonitor/Core/StringProcessor.cs +++ b/RasterPropMonitor/Core/StringProcessor.cs @@ -25,6 +25,10 @@ namespace JSI { public class StringProcessorFormatter { + // The formatString or plain text (if usesComp is false). + private readonly string formatString; + // An array of source variables + internal readonly IVariable[] sourceVariables; internal static readonly SIFormatProvider fp = new SIFormatProvider(); // An array holding evaluants @@ -34,11 +38,6 @@ public class StringProcessorFormatter public string cachedResult; - // An array of source variables - internal readonly IVariable[] sourceVariables; - // The formatString or plain text (if usesComp is false). - private readonly string formatString; - public StringProcessorFormatter(string input, RasterPropMonitorComputer rpmComp, RasterPropMonitor rpm = null) { if (string.IsNullOrEmpty(input)) @@ -46,67 +45,59 @@ public StringProcessorFormatter(string input, RasterPropMonitorComputer rpmComp, cachedResult = ""; return; } - - if (input.IndexOf(JUtil.VariableListSeparator[0], StringComparison.Ordinal) < 0) + else if (input.IndexOf(JUtil.VariableListSeparator[0], StringComparison.Ordinal) >= 0) { - cachedResult = input.TrimEnd(); - return; - } - - string[] tokens = input.Split(JUtil.VariableListSeparator, StringSplitOptions.RemoveEmptyEntries); - if (tokens.Length != 2) - { - throw new ArgumentException(string.Format("Invalid format string: {0}", input)); - } - - bool allVariablesConstant = true; - - string[] sourceVarStrings = tokens[1].Split(JUtil.VariableSeparator, StringSplitOptions.RemoveEmptyEntries); - sourceVariables = new IVariable[sourceVarStrings.Length]; - for (int i = 0; i < sourceVarStrings.Length; ++i) - { - var variableName = sourceVarStrings[i]; - if (variableName.StartsWith("MONITOR_LOCAL_")) + string[] tokens = input.Split(JUtil.VariableListSeparator, StringSplitOptions.RemoveEmptyEntries); + if (tokens.Length != 2) { - sourceVariables[i] = rpm.GetVariable(variableName); + throw new ArgumentException(string.Format("Invalid format string: {0}", input)); } else { - sourceVariables[i] = rpmComp.InstantiateVariableOrNumber(sourceVarStrings[i]); + bool allVariablesConstant = true; + + string[] sourceVarStrings = tokens[1].Split(JUtil.VariableSeparator, StringSplitOptions.RemoveEmptyEntries); + sourceVariables = new IVariable[sourceVarStrings.Length]; + for (int i = 0; i < sourceVarStrings.Length; ++i) + { + var variableName = sourceVarStrings[i]; + if (variableName.StartsWith("MONITOR_LOCAL_")) + { + sourceVariables[i] = rpm.GetVariable(variableName); + } + else + { + sourceVariables[i] = rpmComp.InstantiateVariableOrNumber(sourceVarStrings[i]); + } + + allVariablesConstant = allVariablesConstant && sourceVariables[i].IsConstant(); + } + + sourceValues = new object[sourceVariables.Length]; + formatString = tokens[0].TrimEnd(); + + for (int i = 0; i < sourceVariables.Length; ++i) + { + sourceValues[i] = sourceVariables[i].GetValue(); + } + + cachedResult = string.Format(fp, formatString, sourceValues); + + // if every variable is a constant, we can run the format once and cache the result + if (allVariablesConstant) + { + sourceVariables = null; + sourceValues = null; + } } - - allVariablesConstant = allVariablesConstant && sourceVariables[i].IsConstant(); } - - sourceValues = new object[sourceVariables.Length]; - formatString = tokens[0].TrimEnd(); - - for (int i = 0; i < sourceVariables.Length; ++i) + else { - sourceValues[i] = sourceVariables[i].GetValue(); - } - - cachedResult = string.Format(fp, formatString, sourceValues); - - // if every variable is a constant, we can run the format once and cache the result - if (allVariablesConstant) - { - sourceVariables = null; - sourceValues = null; + cachedResult = input.TrimEnd(); } } - public string GetFormattedString() - { - if (UpdateValues()) - { - cachedResult = string.Format(fp, formatString, sourceValues); - } - - return cachedResult; - } - - private bool UpdateValues() + public bool UpdateValues() { if (sourceValues == null) return false; @@ -123,5 +114,15 @@ private bool UpdateValues() return anyChanged; } + + public string GetFormattedString() + { + if (UpdateValues()) + { + cachedResult = string.Format(fp, formatString, sourceValues); + } + + return cachedResult; + } } } From 0f4b1ab28e56650dae386172b825487c77c7aad7 Mon Sep 17 00:00:00 2001 From: Andrew Pritchard Date: Tue, 30 Sep 2025 21:10:17 +1000 Subject: [PATCH 04/12] Minimise diff 2, remove class, still more to go --- RasterPropMonitor/Core/MonitorPage.cs | 29 +++++++++----- RasterPropMonitor/Core/StringProcessor.cs | 11 +++--- .../Handlers/JSIPatchSelector.cs | 38 ------------------- 3 files changed, 24 insertions(+), 54 deletions(-) delete mode 100644 RasterPropMonitor/Handlers/JSIPatchSelector.cs diff --git a/RasterPropMonitor/Core/MonitorPage.cs b/RasterPropMonitor/Core/MonitorPage.cs index 946afc7f..a8e1552c 100644 --- a/RasterPropMonitor/Core/MonitorPage.cs +++ b/RasterPropMonitor/Core/MonitorPage.cs @@ -22,6 +22,7 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; +using System.Text; using UnityEngine; namespace JSI @@ -32,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; @@ -79,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 PatchSelector patchSelector; + private readonly int buttonNextPatch = INVALID_BUTTON; + private readonly int buttonPrevPatch = INVALID_BUTTON; private struct HandlerSupportMethods { @@ -432,12 +435,15 @@ public MonitorPage(int idNum, ConfigNode node, RasterPropMonitor thatMonitor) } } - if (node.HasNode("PATCHSELECTOR")) + int intValue = INVALID_BUTTON; + if (node.TryGetValue("buttonNextPatch", ref intValue)) { - foreach (ConfigNode patchSelectorNode in node.GetNodes("PATCHSELECTOR")) - { - patchSelector = new PatchSelector(ourMonitor, patchSelectorNode); - } + buttonNextPatch = intValue; + } + + if (node.TryGetValue("buttonPrevPatch", ref intValue)) + { + buttonPrevPatch = intValue; } } @@ -633,7 +639,7 @@ public void Active(bool state) public bool GlobalButtonClick(int buttonID) { buttonID = redirectGlobals[buttonID] ?? buttonID; - if (buttonID == -1) + if (buttonID == INVALID_BUTTON) { return false; } @@ -648,10 +654,13 @@ public bool GlobalButtonClick(int buttonID) backgroundHandlerS.buttonClick(buttonID); actionTaken = true; } - if (patchSelector != null) + if (buttonID == buttonNextPatch) { - patchSelector.HandleButtonPress(buttonID); - actionTaken = true; + ourMonitor.SelectNextPatch(); + } + if (buttonID == buttonPrevPatch) + { + ourMonitor.SelectPreviousPatch(); } return actionTaken; } diff --git a/RasterPropMonitor/Core/StringProcessor.cs b/RasterPropMonitor/Core/StringProcessor.cs index c84035dd..75154d51 100644 --- a/RasterPropMonitor/Core/StringProcessor.cs +++ b/RasterPropMonitor/Core/StringProcessor.cs @@ -19,17 +19,17 @@ * along with RasterPropMonitor. If not, see . ****************************************************************************/ using System; -using System.Diagnostics; +using UnityEngine.Profiling; namespace JSI { public class StringProcessorFormatter { + internal static readonly SIFormatProvider fp = new SIFormatProvider(); // The formatString or plain text (if usesComp is false). private readonly string formatString; // An array of source variables internal readonly IVariable[] sourceVariables; - internal static readonly SIFormatProvider fp = new SIFormatProvider(); // An array holding evaluants public readonly object[] sourceValues; @@ -38,12 +38,12 @@ public class StringProcessorFormatter public string cachedResult; + // TODO: Add support for multi-line processed support. public StringProcessorFormatter(string input, RasterPropMonitorComputer rpmComp, RasterPropMonitor rpm = null) { - if (string.IsNullOrEmpty(input)) + if(string.IsNullOrEmpty(input)) { cachedResult = ""; - return; } else if (input.IndexOf(JUtil.VariableListSeparator[0], StringComparison.Ordinal) >= 0) { @@ -72,7 +72,6 @@ public StringProcessorFormatter(string input, RasterPropMonitorComputer rpmComp, allVariablesConstant = allVariablesConstant && sourceVariables[i].IsConstant(); } - sourceValues = new object[sourceVariables.Length]; formatString = tokens[0].TrimEnd(); @@ -93,7 +92,7 @@ public StringProcessorFormatter(string input, RasterPropMonitorComputer rpmComp, } else { - cachedResult = input.TrimEnd(); + cachedResult = input.TrimEnd(); } } diff --git a/RasterPropMonitor/Handlers/JSIPatchSelector.cs b/RasterPropMonitor/Handlers/JSIPatchSelector.cs deleted file mode 100644 index e108261e..00000000 --- a/RasterPropMonitor/Handlers/JSIPatchSelector.cs +++ /dev/null @@ -1,38 +0,0 @@ -namespace JSI -{ - public class PatchSelector - { - private int buttonNext = 7; - private int buttonPrev = 8; - private RasterPropMonitor rpm; - - internal PatchSelector(RasterPropMonitor rpm, ConfigNode node) - { - this.rpm = rpm; - int intValue = 0; - - if (node.TryGetValue("buttonNext", ref intValue)) - { - buttonNext = intValue; - } - - if (node.TryGetValue("buttonPrev", ref intValue)) - { - buttonPrev = intValue; - } - } - - internal void HandleButtonPress(int buttonID) - { - if (buttonID == buttonNext) - { - rpm.SelectNextPatch(); - } - - if (buttonID == buttonPrev) - { - rpm.SelectPreviousPatch(); - } - } - } -} From 90245993e0ef7934f5e8f38c9b676525aee6ca91 Mon Sep 17 00:00:00 2001 From: Andrew Pritchard Date: Tue, 30 Sep 2025 22:00:15 +1000 Subject: [PATCH 05/12] Reduce diff even further. --- RasterPropMonitor/Core/GenericVariable.cs | 29 ---- RasterPropMonitor/Core/IVariable.cs | 13 -- RasterPropMonitor/Core/RasterPropMonitor.cs | 162 ++++++++++-------- .../Core/RasterPropMonitorComputer.cs | 4 +- RasterPropMonitor/Core/StringProcessor.cs | 27 ++- RasterPropMonitor/Core/VariableOrNumber.cs | 26 +-- 6 files changed, 119 insertions(+), 142 deletions(-) delete mode 100644 RasterPropMonitor/Core/GenericVariable.cs delete mode 100644 RasterPropMonitor/Core/IVariable.cs diff --git a/RasterPropMonitor/Core/GenericVariable.cs b/RasterPropMonitor/Core/GenericVariable.cs deleted file mode 100644 index e3774883..00000000 --- a/RasterPropMonitor/Core/GenericVariable.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; - -namespace JSI -{ - public class GenericVariable : IVariable - { - private Func Generator; - - internal GenericVariable(Func generator) - { - Generator = generator; - } - - object IVariable.GetValue() - { - return Generator(); - } - - bool IVariable.Changed(object oldValue) - { - return true; - } - - bool IVariable.IsConstant() - { - return false; - } - } -} \ No newline at end of file diff --git a/RasterPropMonitor/Core/IVariable.cs b/RasterPropMonitor/Core/IVariable.cs deleted file mode 100644 index 16327830..00000000 --- a/RasterPropMonitor/Core/IVariable.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace JSI -{ - /// - /// This class exists to provide a base class that RasterPropMonitorComputer - /// manages for tracking various built-in plugin action handlers. - /// - internal interface IVariable - { - object GetValue(); - bool Changed(object oldValue); - bool IsConstant(); - } -} diff --git a/RasterPropMonitor/Core/RasterPropMonitor.cs b/RasterPropMonitor/Core/RasterPropMonitor.cs index fdc9ef56..f7030c83 100644 --- a/RasterPropMonitor/Core/RasterPropMonitor.cs +++ b/RasterPropMonitor/Core/RasterPropMonitor.cs @@ -205,8 +205,8 @@ public void Start() // some assumptions about who managed the y-inversion issue between OpenGL and DX9. if (manuallyInvertY) { - screenMat.SetTextureScale(layerID.Trim(), new Vector2(1.0f, -1.0f)); - screenMat.SetTextureOffset(layerID.Trim(), new Vector2(0.0f, 1.0f)); + screenMat.SetTextureScale(layerID.Trim(), new Vector2(1.0f, -1.0f)); + screenMat.SetTextureOffset(layerID.Trim(), new Vector2(0.0f, 1.0f)); } } @@ -358,11 +358,11 @@ internal void SelectPatch(int patchIndex) selectedPatchIndex = effectivePatchIndex; } - internal IVariable GetVariable(string variableName) + internal VariableOrNumber GetVariable(string variableName) { if (variableName == "MONITOR_LOCAL_PATCH_INDEX") { - return new GenericVariable(() => + return CreateVariable(() => { (int index, Orbit _) = GetSelectedPatch(); return index + 1; @@ -370,7 +370,7 @@ internal IVariable GetVariable(string variableName) } if (variableName == "MONITOR_LOCAL_PATCH_COUNT") { - return new GenericVariable(() => + return CreateVariable(() => { (int index, Orbit _) = GetLastPatch(); return index + 1; @@ -378,31 +378,31 @@ internal IVariable GetVariable(string variableName) } if (variableName == "MONITOR_LOCAL_PATCH_ALTITUDE") { - return new GenericVariable(() => GetSelectedPatchOrbit().referenceBody.GetAltitude(vessel.CoM)); + return CreateVariable(() => GetSelectedPatchOrbit().referenceBody.GetAltitude(vessel.CoM)); } if (variableName == "MONITOR_LOCAL_PATCH_ORBTSPEED") { - return new GenericVariable(() => GetSelectedPatchOrbit().GetVel().magnitude); + return CreateVariable(() => GetSelectedPatchOrbit().GetVel().magnitude); } if (variableName == "MONITOR_LOCAL_PATCH_APOAPSIS") { - return new GenericVariable(() => JUtil.OrbitMakesSense(vessel) ? GetSelectedPatchOrbit().ApA : double.NaN); + return CreateVariable(() => JUtil.OrbitMakesSense(vessel) ? GetSelectedPatchOrbit().ApA : double.NaN); } if (variableName == "MONITOR_LOCAL_PATCH_PERIAPSIS") { - return new GenericVariable(() => JUtil.OrbitMakesSense(vessel) ? GetSelectedPatchOrbit().PeA : double.NaN); + return CreateVariable(() => JUtil.OrbitMakesSense(vessel) ? GetSelectedPatchOrbit().PeA : double.NaN); } if (variableName == "MONITOR_LOCAL_PATCH_INCLINATION") { - return new GenericVariable(() => JUtil.OrbitMakesSense(vessel) ? GetSelectedPatchOrbit().inclination : double.NaN); + return CreateVariable(() => JUtil.OrbitMakesSense(vessel) ? GetSelectedPatchOrbit().inclination : double.NaN); } if (variableName == "MONITOR_LOCAL_PATCH_ECCENTRICITY") { - return new GenericVariable(() => JUtil.OrbitMakesSense(vessel) ? GetSelectedPatchOrbit().eccentricity : double.NaN); + return CreateVariable(() => JUtil.OrbitMakesSense(vessel) ? GetSelectedPatchOrbit().eccentricity : double.NaN); } if (variableName == "MONITOR_LOCAL_PATCH_TIMETOAPSECS") { - return new GenericVariable(() => + return CreateVariable(() => { if (!JUtil.OrbitMakesSense(vessel)) return double.NaN; Orbit patch = GetSelectedPatchOrbit(); @@ -413,7 +413,7 @@ internal IVariable GetVariable(string variableName) } if (variableName == "MONITOR_LOCAL_PATCH_TIMETOPESECS") { - return new GenericVariable(() => + return CreateVariable(() => { if (!JUtil.OrbitMakesSense(vessel)) return double.NaN; Orbit patch = GetSelectedPatchOrbit(); @@ -424,15 +424,15 @@ internal IVariable GetVariable(string variableName) } if (variableName == "MONITOR_LOCAL_PATCH_ORBPERIODSECS") { - return new GenericVariable(() => JUtil.OrbitMakesSense(vessel) ? GetSelectedPatchOrbit().period : double.NaN); + return CreateVariable(() => JUtil.OrbitMakesSense(vessel) ? GetSelectedPatchOrbit().period : double.NaN); } if (variableName == "MONITOR_LOCAL_PATCH_ORBITBODY") { - return new GenericVariable(() => GetSelectedPatchOrbit().referenceBody.name); + return CreateStringVariable(() => GetSelectedPatchOrbit().referenceBody.name); } if (variableName == "MONITOR_LOCAL_PATCH_TIMETOANEQUATORIAL") { - return new GenericVariable(() => + return CreateVariable(() => { Orbit patch = GetSelectedPatchOrbit(); if (!JUtil.OrbitMakesSense(vessel) || !patch.AscendingNodeEquatorialExists()) @@ -444,7 +444,7 @@ internal IVariable GetVariable(string variableName) } if (variableName == "MONITOR_LOCAL_PATCH_TIMETODNEQUATORIAL") { - return new GenericVariable(() => + return CreateVariable(() => { Orbit patch = GetSelectedPatchOrbit(); if (!JUtil.OrbitMakesSense(vessel) || !patch.DescendingNodeEquatorialExists()) @@ -456,7 +456,7 @@ internal IVariable GetVariable(string variableName) } if (variableName == "MONITOR_LOCAL_PATCH_FIRST") { - return new GenericVariable(() => + return CreateVariable(() => { (int index, Orbit _) = GetSelectedPatch(); return index == 0 ? 1.0d : 0.0d; @@ -464,7 +464,7 @@ internal IVariable GetVariable(string variableName) } if (variableName == "MONITOR_LOCAL_PATCH_LAST") { - return new GenericVariable(() => + return CreateVariable(() => { (int selected, Orbit _) = GetSelectedPatch(); (int last, Orbit _) = GetLastPatch(); @@ -473,7 +473,7 @@ internal IVariable GetVariable(string variableName) } if (variableName == "MONITOR_LOCAL_PATCH_NEXTAPSISTYPE") { - return new GenericVariable(() => + return CreateVariable(() => { Orbit patch = GetSelectedPatchOrbit(); if (patch.eccentricity < 1.0) @@ -489,7 +489,7 @@ internal IVariable GetVariable(string variableName) } if (variableName == "MONITOR_LOCAL_PATCH_NEXT_ANDN_EQUATORIAL") { - return new GenericVariable(() => + return CreateVariable(() => { Orbit patch = GetSelectedPatchOrbit(); double universalTime = Planetarium.GetUniversalTime(); @@ -505,7 +505,7 @@ internal IVariable GetVariable(string variableName) } if (variableName == "MONITOR_LOCAL_PATCH_ENCOUNTEREXISTS") { - return new GenericVariable(() => + return CreateVariable(() => { if (!JUtil.OrbitMakesSense(vessel)) return 0.0; Orbit patch = GetSelectedPatchOrbit(); @@ -522,7 +522,7 @@ internal IVariable GetVariable(string variableName) } if (variableName == "MONITOR_LOCAL_PATCH_ENCOUNTERTIME") { - return new GenericVariable(() => + return CreateVariable(() => { if (!JUtil.OrbitMakesSense(vessel)) return 0.0; Orbit patch = GetSelectedPatchOrbit(); @@ -536,9 +536,9 @@ internal IVariable GetVariable(string variableName) } if (variableName == "MONITOR_LOCAL_PATCH_ENCOUNTERBODY") { - return new GenericVariable(() => + return CreateStringVariable(() => { - if (!JUtil.OrbitMakesSense(vessel)) return 0.0; + if (!JUtil.OrbitMakesSense(vessel)) return string.Empty; Orbit patch = GetSelectedPatchOrbit(); switch (patch.patchEndTransition) { @@ -647,53 +647,53 @@ private bool UpdateCheck() private void RenderScreen() { - Profiler.BeginSample("RPM.RenderScreen [" + activePage.name + "]"); + Profiler.BeginSample("RPM.RenderScreen [" + activePage.name + "]"); - RenderTexture backupRenderTexture = RenderTexture.active; + RenderTexture backupRenderTexture = RenderTexture.active; if (!screenTexture.IsCreated()) { screenTexture.Create(); } - - if (resourceDepleted || noCommConnection) - { + + if (resourceDepleted || noCommConnection) + { screenTexture.DiscardContents(); RenderTexture.active = screenTexture; // If we're out of electric charge, we're drawing a blank screen. GL.Clear(true, true, emptyColorValue); - } - else if (textRenderer.UpdateText(activePage) || activePage.background == MonitorPage.BackgroundType.Handler) - { + } + else if (textRenderer.UpdateText(activePage) || activePage.background == MonitorPage.BackgroundType.Handler) + { screenTexture.DiscardContents(); RenderTexture.active = screenTexture; // This is the important witchcraft. Without that, DrawTexture does not print where we expect it to. // Cameras don't care because they have their own matrices, but DrawTexture does. GL.PushMatrix(); - GL.LoadPixelMatrix(0, screenPixelWidth, screenPixelHeight, 0); + GL.LoadPixelMatrix(0, screenPixelWidth, screenPixelHeight, 0); - // Actual rendering of the background is delegated to the page object. - activePage.RenderBackground(screenTexture); + // Actual rendering of the background is delegated to the page object. + activePage.RenderBackground(screenTexture); - if (!string.IsNullOrEmpty(activePage.ProcessedText)) - { - textRenderer.Render(screenTexture); - } + if (!string.IsNullOrEmpty(activePage.ProcessedText)) + { + textRenderer.Render(screenTexture); + } - activePage.RenderOverlay(screenTexture); - GL.PopMatrix(); - } + activePage.RenderOverlay(screenTexture); + GL.PopMatrix(); + } - RenderTexture.active = backupRenderTexture; - Profiler.EndSample(); - } + RenderTexture.active = backupRenderTexture; + Profiler.EndSample(); + } private void FillScreenBuffer() { - Profiler.BeginSample("RasterPropMonitor.FillScreenBuffer"); - activePage.UpdateText(rpmComp); - Profiler.EndSample(); + Profiler.BeginSample("RasterPropMonitor.FillScreenBuffer"); + activePage.UpdateText(rpmComp); + Profiler.EndSample(); } public void LateUpdate() @@ -710,7 +710,7 @@ public void LateUpdate() { return; } - + if (!JUtil.RasterPropMonitorShouldUpdate(part)) { return; @@ -739,7 +739,7 @@ public void LateUpdate() return; } - Profiler.BeginSample("RasterPropMonitor.OnLateUpdate"); + Profiler.BeginSample("RasterPropMonitor.OnLateUpdate"); if (resourceDepleted || noCommConnection) { @@ -748,8 +748,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) { @@ -771,23 +771,23 @@ public void LateUpdate() firstRenderComplete = true; } - // Oneshot screens: We create a permanent texture from our RenderTexture if the first pass of the render is complete, - // set it in place of the rendertexture -- and then we selfdestruct. - // MOARdV: Except we don't want to self-destruct, because we will leak the frozenScreen texture. - if (oneshot && firstRenderComplete) - { - frozenScreen = new Texture2D(screenTexture.width, screenTexture.height); - RenderTexture backupRenderTexture = RenderTexture.active; - RenderTexture.active = screenTexture; - frozenScreen.ReadPixels(new Rect(0, 0, screenTexture.width, screenTexture.height), 0, 0); - RenderTexture.active = backupRenderTexture; - foreach (string layerID in textureLayerID.Split()) - { - screenMat.SetTexture(layerID.Trim(), frozenScreen); - } - } - - Profiler.EndSample(); + // Oneshot screens: We create a permanent texture from our RenderTexture if the first pass of the render is complete, + // set it in place of the rendertexture -- and then we selfdestruct. + // MOARdV: Except we don't want to self-destruct, because we will leak the frozenScreen texture. + if (oneshot && firstRenderComplete) + { + frozenScreen = new Texture2D(screenTexture.width, screenTexture.height); + RenderTexture backupRenderTexture = RenderTexture.active; + RenderTexture.active = screenTexture; + frozenScreen.ReadPixels(new Rect(0, 0, screenTexture.width, screenTexture.height), 0, 0); + RenderTexture.active = backupRenderTexture; + foreach (string layerID in textureLayerID.Split()) + { + screenMat.SetTexture(layerID.Trim(), frozenScreen); + } + } + + Profiler.EndSample(); } public void OnApplicationPause(bool pause) @@ -860,6 +860,28 @@ void CommConnectionCallback(float newValue) return (effectivePatchIndex, patch); } + + private VariableOrNumber CreateVariable(Func evaluator) + { + return new VariableOrNumber( + "", + (RPMVesselComputer _) => evaluator(), + null, + VariableUpdateType.Volatile, + rpmComp + ); + } + + private VariableOrNumber CreateStringVariable(Func evaluator) + { + return new VariableOrNumber( + "", + (RPMVesselComputer _) => evaluator(), + null, + VariableUpdateType.Volatile, + rpmComp + ); + } } } diff --git a/RasterPropMonitor/Core/RasterPropMonitorComputer.cs b/RasterPropMonitor/Core/RasterPropMonitorComputer.cs index 38bbd032..2967f990 100644 --- a/RasterPropMonitor/Core/RasterPropMonitorComputer.cs +++ b/RasterPropMonitor/Core/RasterPropMonitorComputer.cs @@ -151,7 +151,7 @@ public void RegisterVariableCallback(string variableName, Action cb) { var vc = InstantiateVariableOrNumber(variableName); vc.onChangeCallbacks += cb; - cb((float)vc.numericValue); + cb(vc.AsFloat()); } /// @@ -178,7 +178,7 @@ public void RegisterResourceCallback(string variableName, Action cb) { var vc = InstantiateVariableOrNumber(variableName); vc.onResourceDepletedCallbacks += cb; - cb(vc.numericValue < 0.01); + cb(vc.AsDouble() < 0.01); } /// diff --git a/RasterPropMonitor/Core/StringProcessor.cs b/RasterPropMonitor/Core/StringProcessor.cs index 75154d51..cab011f9 100644 --- a/RasterPropMonitor/Core/StringProcessor.cs +++ b/RasterPropMonitor/Core/StringProcessor.cs @@ -29,7 +29,7 @@ public class StringProcessorFormatter // The formatString or plain text (if usesComp is false). private readonly string formatString; // An array of source variables - internal readonly IVariable[] sourceVariables; + internal readonly VariableOrNumber[] sourceVariables; // An array holding evaluants public readonly object[] sourceValues; @@ -57,7 +57,7 @@ public StringProcessorFormatter(string input, RasterPropMonitorComputer rpmComp, bool allVariablesConstant = true; string[] sourceVarStrings = tokens[1].Split(JUtil.VariableSeparator, StringSplitOptions.RemoveEmptyEntries); - sourceVariables = new IVariable[sourceVarStrings.Length]; + sourceVariables = new VariableOrNumber[sourceVarStrings.Length]; for (int i = 0; i < sourceVarStrings.Length; ++i) { var variableName = sourceVarStrings[i]; @@ -70,14 +70,14 @@ public StringProcessorFormatter(string input, RasterPropMonitorComputer rpmComp, sourceVariables[i] = rpmComp.InstantiateVariableOrNumber(sourceVarStrings[i]); } - allVariablesConstant = allVariablesConstant && sourceVariables[i].IsConstant(); + allVariablesConstant = allVariablesConstant && sourceVariables[i].isConstant; } sourceValues = new object[sourceVariables.Length]; formatString = tokens[0].TrimEnd(); for (int i = 0; i < sourceVariables.Length; ++i) { - sourceValues[i] = sourceVariables[i].GetValue(); + sourceValues[i] = sourceVariables[i].Get(); } cachedResult = string.Format(fp, formatString, sourceValues); @@ -104,10 +104,23 @@ public bool UpdateValues() for (int i = 0; i < sourceVariables.Length; ++i) { var sourceVariable = sourceVariables[i]; - if (!sourceVariable.IsConstant()) + if (!sourceVariable.isConstant) { - anyChanged = anyChanged || sourceVariable.Changed(sourceValues[i]); - sourceValues[i] = sourceVariable.GetValue(); + if (sourceVariable.isNumeric) + { + double newValue = (double)sourceVariable.Get(); + if (JUtil.ValueChanged((double)sourceValues[i], newValue)) + { + anyChanged = true; + sourceValues[i] = newValue; + } + } + else + { + string newValue = (string)sourceVariable.Get(); + anyChanged = anyChanged || newValue != (string)sourceValues[i]; + sourceValues[i] = newValue; + } } } diff --git a/RasterPropMonitor/Core/VariableOrNumber.cs b/RasterPropMonitor/Core/VariableOrNumber.cs index 3d998236..08bb955c 100644 --- a/RasterPropMonitor/Core/VariableOrNumber.cs +++ b/RasterPropMonitor/Core/VariableOrNumber.cs @@ -38,11 +38,9 @@ enum VariableUpdateType /// It's owned by the VariableCache instances inside the RasterPropMonitorComputer /// This architecture is kind of strange; this could probably be unified with VariableCache /// - public class VariableOrNumber : IVariable + public class VariableOrNumber { internal readonly string variableName; - internal double numericValue; - internal string stringValue; internal bool isNumeric; internal VariableUpdateType updateType; private readonly RasterPropMonitorComputer rpmComp; @@ -54,6 +52,9 @@ public class VariableOrNumber : IVariable public bool isConstant => updateType == VariableUpdateType.Constant; + private double numericValue; + private string stringValue; + internal void FireCallbacks(float newValue) { if (onChangeCallbacks != null) @@ -220,7 +221,7 @@ public int AsInt() /// Return the value boxed as an object /// /// - object IVariable.GetValue() + public object Get() { if (updateType == VariableUpdateType.Volatile) { @@ -240,23 +241,6 @@ object IVariable.GetValue() return stringValue; } } - - bool IVariable.Changed(object oldValue) - { - if (isNumeric) - { - return JUtil.ValueChanged((double)oldValue, numericValue); - } - else - { - return stringValue != (string)oldValue; - } - } - - bool IVariable.IsConstant() - { - return isConstant; - } } /// From 083973c3ba0a12557f57c6158deb153df689ec02 Mon Sep 17 00:00:00 2001 From: Andrew Pritchard Date: Tue, 30 Sep 2025 23:00:32 +1000 Subject: [PATCH 06/12] Remove whitespace changes, fix btton click sound --- RasterPropMonitor/Core/RasterPropMonitor.cs | 94 +++++++++---------- RasterPropMonitor/Core/StringProcessor.cs | 8 +- RasterPropMonitor/Handlers/JSIOrbitDisplay.cs | 7 +- 3 files changed, 54 insertions(+), 55 deletions(-) diff --git a/RasterPropMonitor/Core/RasterPropMonitor.cs b/RasterPropMonitor/Core/RasterPropMonitor.cs index f7030c83..3d4c75ff 100644 --- a/RasterPropMonitor/Core/RasterPropMonitor.cs +++ b/RasterPropMonitor/Core/RasterPropMonitor.cs @@ -205,8 +205,8 @@ public void Start() // some assumptions about who managed the y-inversion issue between OpenGL and DX9. if (manuallyInvertY) { - screenMat.SetTextureScale(layerID.Trim(), new Vector2(1.0f, -1.0f)); - screenMat.SetTextureOffset(layerID.Trim(), new Vector2(0.0f, 1.0f)); + screenMat.SetTextureScale(layerID.Trim(), new Vector2(1.0f, -1.0f)); + screenMat.SetTextureOffset(layerID.Trim(), new Vector2(0.0f, 1.0f)); } } @@ -647,53 +647,53 @@ private bool UpdateCheck() private void RenderScreen() { - Profiler.BeginSample("RPM.RenderScreen [" + activePage.name + "]"); + Profiler.BeginSample("RPM.RenderScreen [" + activePage.name + "]"); - RenderTexture backupRenderTexture = RenderTexture.active; + RenderTexture backupRenderTexture = RenderTexture.active; if (!screenTexture.IsCreated()) { screenTexture.Create(); } - - if (resourceDepleted || noCommConnection) - { + + if (resourceDepleted || noCommConnection) + { screenTexture.DiscardContents(); RenderTexture.active = screenTexture; // If we're out of electric charge, we're drawing a blank screen. GL.Clear(true, true, emptyColorValue); - } - else if (textRenderer.UpdateText(activePage) || activePage.background == MonitorPage.BackgroundType.Handler) - { + } + else if (textRenderer.UpdateText(activePage) || activePage.background == MonitorPage.BackgroundType.Handler) + { screenTexture.DiscardContents(); RenderTexture.active = screenTexture; // This is the important witchcraft. Without that, DrawTexture does not print where we expect it to. // Cameras don't care because they have their own matrices, but DrawTexture does. GL.PushMatrix(); - GL.LoadPixelMatrix(0, screenPixelWidth, screenPixelHeight, 0); + GL.LoadPixelMatrix(0, screenPixelWidth, screenPixelHeight, 0); - // Actual rendering of the background is delegated to the page object. - activePage.RenderBackground(screenTexture); + // Actual rendering of the background is delegated to the page object. + activePage.RenderBackground(screenTexture); - if (!string.IsNullOrEmpty(activePage.ProcessedText)) - { - textRenderer.Render(screenTexture); - } + if (!string.IsNullOrEmpty(activePage.ProcessedText)) + { + textRenderer.Render(screenTexture); + } - activePage.RenderOverlay(screenTexture); - GL.PopMatrix(); - } + activePage.RenderOverlay(screenTexture); + GL.PopMatrix(); + } - RenderTexture.active = backupRenderTexture; - Profiler.EndSample(); - } + RenderTexture.active = backupRenderTexture; + Profiler.EndSample(); + } private void FillScreenBuffer() { - Profiler.BeginSample("RasterPropMonitor.FillScreenBuffer"); - activePage.UpdateText(rpmComp); - Profiler.EndSample(); + Profiler.BeginSample("RasterPropMonitor.FillScreenBuffer"); + activePage.UpdateText(rpmComp); + Profiler.EndSample(); } public void LateUpdate() @@ -710,7 +710,7 @@ public void LateUpdate() { return; } - + if (!JUtil.RasterPropMonitorShouldUpdate(part)) { return; @@ -739,7 +739,7 @@ public void LateUpdate() return; } - Profiler.BeginSample("RasterPropMonitor.OnLateUpdate"); + Profiler.BeginSample("RasterPropMonitor.OnLateUpdate"); if (resourceDepleted || noCommConnection) { @@ -748,8 +748,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) { @@ -771,23 +771,23 @@ public void LateUpdate() firstRenderComplete = true; } - // Oneshot screens: We create a permanent texture from our RenderTexture if the first pass of the render is complete, - // set it in place of the rendertexture -- and then we selfdestruct. - // MOARdV: Except we don't want to self-destruct, because we will leak the frozenScreen texture. - if (oneshot && firstRenderComplete) - { - frozenScreen = new Texture2D(screenTexture.width, screenTexture.height); - RenderTexture backupRenderTexture = RenderTexture.active; - RenderTexture.active = screenTexture; - frozenScreen.ReadPixels(new Rect(0, 0, screenTexture.width, screenTexture.height), 0, 0); - RenderTexture.active = backupRenderTexture; - foreach (string layerID in textureLayerID.Split()) - { - screenMat.SetTexture(layerID.Trim(), frozenScreen); - } - } - - Profiler.EndSample(); + // Oneshot screens: We create a permanent texture from our RenderTexture if the first pass of the render is complete, + // set it in place of the rendertexture -- and then we selfdestruct. + // MOARdV: Except we don't want to self-destruct, because we will leak the frozenScreen texture. + if (oneshot && firstRenderComplete) + { + frozenScreen = new Texture2D(screenTexture.width, screenTexture.height); + RenderTexture backupRenderTexture = RenderTexture.active; + RenderTexture.active = screenTexture; + frozenScreen.ReadPixels(new Rect(0, 0, screenTexture.width, screenTexture.height), 0, 0); + RenderTexture.active = backupRenderTexture; + foreach (string layerID in textureLayerID.Split()) + { + screenMat.SetTexture(layerID.Trim(), frozenScreen); + } + } + + Profiler.EndSample(); } public void OnApplicationPause(bool pause) diff --git a/RasterPropMonitor/Core/StringProcessor.cs b/RasterPropMonitor/Core/StringProcessor.cs index cab011f9..3498c7b5 100644 --- a/RasterPropMonitor/Core/StringProcessor.cs +++ b/RasterPropMonitor/Core/StringProcessor.cs @@ -26,11 +26,11 @@ namespace JSI public class StringProcessorFormatter { internal static readonly SIFormatProvider fp = new SIFormatProvider(); + // The formatString or plain text (if usesComp is false). private readonly string formatString; // An array of source variables - internal readonly VariableOrNumber[] sourceVariables; - + public readonly VariableOrNumber[] sourceVariables; // An array holding evaluants public readonly object[] sourceValues; @@ -58,7 +58,7 @@ public StringProcessorFormatter(string input, RasterPropMonitorComputer rpmComp, string[] sourceVarStrings = tokens[1].Split(JUtil.VariableSeparator, StringSplitOptions.RemoveEmptyEntries); sourceVariables = new VariableOrNumber[sourceVarStrings.Length]; - for (int i = 0; i < sourceVarStrings.Length; ++i) + for (int i = 0; i < sourceVarStrings.Length; ++i ) { var variableName = sourceVarStrings[i]; if (variableName.StartsWith("MONITOR_LOCAL_")) @@ -67,7 +67,7 @@ public StringProcessorFormatter(string input, RasterPropMonitorComputer rpmComp, } else { - sourceVariables[i] = rpmComp.InstantiateVariableOrNumber(sourceVarStrings[i]); + sourceVariables[i] = rpmComp.InstantiateVariableOrNumber(variableName); } allVariablesConstant = allVariablesConstant && sourceVariables[i].isConstant; diff --git a/RasterPropMonitor/Handlers/JSIOrbitDisplay.cs b/RasterPropMonitor/Handlers/JSIOrbitDisplay.cs index a691589d..cb86ccd3 100644 --- a/RasterPropMonitor/Handlers/JSIOrbitDisplay.cs +++ b/RasterPropMonitor/Handlers/JSIOrbitDisplay.cs @@ -22,7 +22,7 @@ // JSIOrbitDisplay: Display a schematic (line-art) drawing of the vessel's // orbit, marking highlights (Pe, Ap, AN, DN), along with the mainbody's // surface and atmosphere (if applicable). -using System; +using System; using UnityEngine; namespace JSI @@ -66,8 +66,8 @@ public class JSIOrbitDisplay : InternalModule, IPageElement private Material lineMaterial; private RasterPropMonitor rpm; - private static readonly int CIRCLE_POINTS = 60; - private static readonly int ORBIT_POINTS = 60; + static readonly int CIRCLE_POINTS = 60; + static readonly int ORBIT_POINTS = 60; public override void OnAwake() { @@ -204,7 +204,6 @@ private static void DrawOrbitSegment( DrawOrbitSegment(o, referenceBody, screenTransform, now, midTA, midVertex, endTA, endVertex); } - // Fallback method: The orbit should be valid, but it's not showing as // active. I've encountered this when targeting a vessel or planet. private static void ReallyDrawOrbit(Orbit o, CelestialBody referenceBody, Matrix4x4 screenTransform) From 7b8e25819545887325a2cb1f3b590b251dd359cf Mon Sep 17 00:00:00 2001 From: Andrew Pritchard Date: Tue, 30 Sep 2025 23:00:52 +1000 Subject: [PATCH 07/12] fix button click sound --- RasterPropMonitor/Core/MonitorPage.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/RasterPropMonitor/Core/MonitorPage.cs b/RasterPropMonitor/Core/MonitorPage.cs index a8e1552c..0a14fe56 100644 --- a/RasterPropMonitor/Core/MonitorPage.cs +++ b/RasterPropMonitor/Core/MonitorPage.cs @@ -657,10 +657,12 @@ public bool GlobalButtonClick(int buttonID) if (buttonID == buttonNextPatch) { ourMonitor.SelectNextPatch(); + actionTaken = true; } if (buttonID == buttonPrevPatch) { ourMonitor.SelectPreviousPatch(); + actionTaken = true; } return actionTaken; } From 8591aac6fd74ac895f808819793e71d1848c13f4 Mon Sep 17 00:00:00 2001 From: Andrew Pritchard Date: Tue, 30 Sep 2025 23:06:27 +1000 Subject: [PATCH 08/12] Revert whitespace changes --- RasterPropMonitor/Handlers/JSIOrbitDisplay.cs | 71 ++++++++++--------- 1 file changed, 36 insertions(+), 35 deletions(-) diff --git a/RasterPropMonitor/Handlers/JSIOrbitDisplay.cs b/RasterPropMonitor/Handlers/JSIOrbitDisplay.cs index cb86ccd3..6a88deb7 100644 --- a/RasterPropMonitor/Handlers/JSIOrbitDisplay.cs +++ b/RasterPropMonitor/Handlers/JSIOrbitDisplay.cs @@ -29,6 +29,7 @@ namespace JSI { public class JSIOrbitDisplay : InternalModule, IPageElement { + [KSPField] public string backgroundColor = string.Empty; private Color backgroundColorValue = Color.black; [KSPField] @@ -63,21 +64,21 @@ public class JSIOrbitDisplay : InternalModule, IPageElement public Vector2 iconShadowShift = new Vector2(1, 1); private bool startupComplete; - private Material lineMaterial; + private Material lineMaterial; private RasterPropMonitor rpm; static readonly int CIRCLE_POINTS = 60; static readonly int ORBIT_POINTS = 60; - public override void OnAwake() - { - base.OnAwake(); + public override void OnAwake() + { + base.OnAwake(); - if (lineMaterial == null) - { - lineMaterial = JUtil.DrawLineMaterial(); - } - } + if (lineMaterial == null) + { + lineMaterial = JUtil.DrawLineMaterial(); + } + } void IPageElement.HandlePageCreate(RasterPropMonitor rpm) { @@ -712,14 +713,14 @@ private void DrawIcon(float xPos, float yPos, VesselType vt, Color iconColor, Ma //MapView.OrbitIconsMaterial.color = iconColorShadowValue; //Graphics.DrawTexture(shadow, MapView.OrbitIconsMap, MapIcons.VesselTypeIcon(vt, icon), 0, 0, 0, 0, MapView.OrbitIconsMaterial); - // the old icon material wasn't working, so just use this one - // but I don't fully understand the color/blend system - // a = 1.0 is far too faint; 4.0 looks pretty good + // the old icon material wasn't working, so just use this one + // but I don't fully understand the color/blend system + // a = 1.0 is far too faint; 4.0 looks pretty good MapView.OrbitIconsMaterial.color = new Color(iconColor.r, iconColor.g, iconColor.b, 4.0f); Graphics.DrawTexture(position, MapView.OrbitIconsMap, MapIcons.VesselTypeIcon(vt, icon), 0, 0, 0, 0, MapView.OrbitIconsMaterial); - - // if the icon texture ever changes, you can use this code to dump it out for inspection -#if false + + // if the icon texture ever changes, you can use this code to dump it out for inspection + #if false var filepath = "orbiticonsmap.png"; if (!System.IO.File.Exists(filepath)) { @@ -727,28 +728,28 @@ private void DrawIcon(float xPos, float yPos, VesselType vt, Color iconColor, Ma var textureBytes = textureCopy.EncodeToPNG(); System.IO.File.WriteAllBytes(filepath, textureBytes); } -#endif + #endif } - Texture2D duplicateTexture(Texture2D source) - { - RenderTexture renderTex = RenderTexture.GetTemporary( - source.width, - source.height, - 0, - RenderTextureFormat.Default, - RenderTextureReadWrite.Linear); - - Graphics.Blit(source, renderTex); - RenderTexture previous = RenderTexture.active; - RenderTexture.active = renderTex; - Texture2D readableText = new Texture2D(source.width, source.height); - readableText.ReadPixels(new Rect(0, 0, renderTex.width, renderTex.height), 0, 0); - readableText.Apply(); - RenderTexture.active = previous; - RenderTexture.ReleaseTemporary(renderTex); - return readableText; - } + Texture2D duplicateTexture(Texture2D source) + { + RenderTexture renderTex = RenderTexture.GetTemporary( + source.width, + source.height, + 0, + RenderTextureFormat.Default, + RenderTextureReadWrite.Linear); + + Graphics.Blit(source, renderTex); + RenderTexture previous = RenderTexture.active; + RenderTexture.active = renderTex; + Texture2D readableText = new Texture2D(source.width, source.height); + readableText.ReadPixels(new Rect(0, 0, renderTex.width, renderTex.height), 0, 0); + readableText.Apply(); + RenderTexture.active = previous; + RenderTexture.ReleaseTemporary(renderTex); + return readableText; + } public void Start() { From 1cab95657156c947325b5ab114b65402cc0136d3 Mon Sep 17 00:00:00 2001 From: Andrew Pritchard Date: Wed, 1 Oct 2025 14:44:00 +1000 Subject: [PATCH 09/12] Render next patch. This wasn't working previously until the draw code was updated --- RasterPropMonitor/Handlers/JSIOrbitDisplay.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/RasterPropMonitor/Handlers/JSIOrbitDisplay.cs b/RasterPropMonitor/Handlers/JSIOrbitDisplay.cs index 6a88deb7..0c0ac836 100644 --- a/RasterPropMonitor/Handlers/JSIOrbitDisplay.cs +++ b/RasterPropMonitor/Handlers/JSIOrbitDisplay.cs @@ -545,12 +545,11 @@ public bool RenderOrbit(RenderTexture screen, float cameraAspect) DrawOrbit(node.nextPatch, selectedPatch.referenceBody, screenTransform); } - // TODO fix this up - // if (selectedPatch.nextPatch != null && selectedPatch.nextPatch.activePatch) - // { - // GL.Color(orbitColorNextNodeValue); - // DrawOrbit(selectedPatch.nextPatch, selectedPatch.referenceBody, screenTransform); - // } + if (selectedPatch.nextPatch != null && selectedPatch.nextPatch.activePatch) + { + GL.Color(orbitColorNextNodeValue); + DrawOrbit(selectedPatch.nextPatch, selectedPatch.referenceBody, screenTransform); + } // Draw the vessel orbit GL.Color(orbitColorSelfValue); From 751cb68f7ed034b3cb2d292f624b4427c0184144 Mon Sep 17 00:00:00 2001 From: Andrew Pritchard Date: Thu, 2 Oct 2025 07:33:09 +1000 Subject: [PATCH 10/12] Made vairables pod local, not monitor local --- RasterPropMonitor/Core/IPageElement.cs | 7 - RasterPropMonitor/Core/MonitorPage.cs | 5 - RasterPropMonitor/Core/RPMCEvaluators.cs | 149 ++++++++- RasterPropMonitor/Core/RasterPropMonitor.cs | 296 +----------------- .../Core/RasterPropMonitorComputer.cs | 64 ++++ RasterPropMonitor/Core/StringProcessor.cs | 11 +- RasterPropMonitor/Handlers/JSIOrbitDisplay.cs | 11 +- 7 files changed, 228 insertions(+), 315 deletions(-) delete mode 100644 RasterPropMonitor/Core/IPageElement.cs diff --git a/RasterPropMonitor/Core/IPageElement.cs b/RasterPropMonitor/Core/IPageElement.cs deleted file mode 100644 index 26cbf3a8..00000000 --- a/RasterPropMonitor/Core/IPageElement.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace JSI -{ - internal interface IPageElement - { - void HandlePageCreate(RasterPropMonitor propMonitor); - } -} diff --git a/RasterPropMonitor/Core/MonitorPage.cs b/RasterPropMonitor/Core/MonitorPage.cs index 0a14fe56..1127e70f 100644 --- a/RasterPropMonitor/Core/MonitorPage.cs +++ b/RasterPropMonitor/Core/MonitorPage.cs @@ -605,11 +605,6 @@ private static MethodInfo InstantiateHandler(ConfigNode node, RasterPropMonitor } } - if (thatModule is IPageElement pageElement) - { - pageElement.HandlePageCreate(ourMonitor); - } - moduleInstance = thatModule; foreach (MethodInfo m in thatModule.GetType().GetMethods()) { diff --git a/RasterPropMonitor/Core/RPMCEvaluators.cs b/RasterPropMonitor/Core/RPMCEvaluators.cs index 65787db8..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; @@ -2628,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 3d4c75ff..a8410907 100644 --- a/RasterPropMonitor/Core/RasterPropMonitor.cs +++ b/RasterPropMonitor/Core/RasterPropMonitor.cs @@ -105,7 +105,6 @@ public class RasterPropMonitor : InternalModule private bool startupComplete; private string fontDefinitionString = @" !""#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~Δ☊¡¢£¤¥¦§¨©ª«¬☋®¯°±²³´µ¶·¸¹º»¼½¾¿"; private RasterPropMonitorComputer rpmComp; - private int selectedPatchIndex; private static Texture2D LoadFont(object caller, InternalProp thisProp, string location) { @@ -315,245 +314,6 @@ public void OnDestroy() rpmComp.UnregisterVariableCallback("COMMNETVESSELCONTROLSTATE", CommConnectionCallback); } - /// - /// 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); - } - - internal void SelectPatch(int patchIndex) - { - (int effectivePatchIndex, _) = EffectivePatch(patchIndex); - selectedPatchIndex = effectivePatchIndex; - } - - internal VariableOrNumber GetVariable(string variableName) - { - if (variableName == "MONITOR_LOCAL_PATCH_INDEX") - { - return CreateVariable(() => - { - (int index, Orbit _) = GetSelectedPatch(); - return index + 1; - }); - } - if (variableName == "MONITOR_LOCAL_PATCH_COUNT") - { - return CreateVariable(() => - { - (int index, Orbit _) = GetLastPatch(); - return index + 1; - }); - } - if (variableName == "MONITOR_LOCAL_PATCH_ALTITUDE") - { - return CreateVariable(() => GetSelectedPatchOrbit().referenceBody.GetAltitude(vessel.CoM)); - } - if (variableName == "MONITOR_LOCAL_PATCH_ORBTSPEED") - { - return CreateVariable(() => GetSelectedPatchOrbit().GetVel().magnitude); - } - if (variableName == "MONITOR_LOCAL_PATCH_APOAPSIS") - { - return CreateVariable(() => JUtil.OrbitMakesSense(vessel) ? GetSelectedPatchOrbit().ApA : double.NaN); - } - if (variableName == "MONITOR_LOCAL_PATCH_PERIAPSIS") - { - return CreateVariable(() => JUtil.OrbitMakesSense(vessel) ? GetSelectedPatchOrbit().PeA : double.NaN); - } - if (variableName == "MONITOR_LOCAL_PATCH_INCLINATION") - { - return CreateVariable(() => JUtil.OrbitMakesSense(vessel) ? GetSelectedPatchOrbit().inclination : double.NaN); - } - if (variableName == "MONITOR_LOCAL_PATCH_ECCENTRICITY") - { - return CreateVariable(() => JUtil.OrbitMakesSense(vessel) ? GetSelectedPatchOrbit().eccentricity : double.NaN); - } - if (variableName == "MONITOR_LOCAL_PATCH_TIMETOAPSECS") - { - return CreateVariable(() => - { - 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(); - }); - } - if (variableName == "MONITOR_LOCAL_PATCH_TIMETOPESECS") - { - return CreateVariable(() => - { - 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(); - }); - } - if (variableName == "MONITOR_LOCAL_PATCH_ORBPERIODSECS") - { - return CreateVariable(() => JUtil.OrbitMakesSense(vessel) ? GetSelectedPatchOrbit().period : double.NaN); - } - if (variableName == "MONITOR_LOCAL_PATCH_ORBITBODY") - { - return CreateStringVariable(() => GetSelectedPatchOrbit().referenceBody.name); - } - if (variableName == "MONITOR_LOCAL_PATCH_TIMETOANEQUATORIAL") - { - return CreateVariable(() => - { - Orbit patch = GetSelectedPatchOrbit(); - if (!JUtil.OrbitMakesSense(vessel) || !patch.AscendingNodeEquatorialExists()) - { - return double.NaN; - } - return patch.TimeOfAscendingNodeEquatorial(Planetarium.GetUniversalTime()) - Planetarium.GetUniversalTime(); - }); - } - if (variableName == "MONITOR_LOCAL_PATCH_TIMETODNEQUATORIAL") - { - return CreateVariable(() => - { - Orbit patch = GetSelectedPatchOrbit(); - if (!JUtil.OrbitMakesSense(vessel) || !patch.DescendingNodeEquatorialExists()) - { - return double.NaN; - } - return patch.TimeOfDescendingNodeEquatorial(Planetarium.GetUniversalTime()) - Planetarium.GetUniversalTime(); - }); - } - if (variableName == "MONITOR_LOCAL_PATCH_FIRST") - { - return CreateVariable(() => - { - (int index, Orbit _) = GetSelectedPatch(); - return index == 0 ? 1.0d : 0.0d; - }); - } - if (variableName == "MONITOR_LOCAL_PATCH_LAST") - { - return CreateVariable(() => - { - (int selected, Orbit _) = GetSelectedPatch(); - (int last, Orbit _) = GetLastPatch(); - return selected == last ? 1.0d : 0.0d; - }); - } - if (variableName == "MONITOR_LOCAL_PATCH_NEXTAPSISTYPE") - { - return CreateVariable(() => - { - 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; - }); - } - if (variableName == "MONITOR_LOCAL_PATCH_NEXT_ANDN_EQUATORIAL") - { - return CreateVariable(() => - { - 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; - }); - } - if (variableName == "MONITOR_LOCAL_PATCH_ENCOUNTEREXISTS") - { - return CreateVariable(() => - { - 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; - } - }); - } - if (variableName == "MONITOR_LOCAL_PATCH_ENCOUNTERTIME") - { - return CreateVariable(() => - { - 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; - }); - } - if (variableName == "MONITOR_LOCAL_PATCH_ENCOUNTERBODY") - { - return CreateStringVariable(() => - { - 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; - }); - } - - return null; - } - private static void PlayClickSound(FXGroup audioOutput) { if (audioOutput != null) @@ -619,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() { @@ -748,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) { @@ -840,48 +610,6 @@ void CommConnectionCallback(float newValue) noCommConnection = false; } } - - /// - /// 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); - } - - private VariableOrNumber CreateVariable(Func evaluator) - { - return new VariableOrNumber( - "", - (RPMVesselComputer _) => evaluator(), - null, - VariableUpdateType.Volatile, - rpmComp - ); - } - - private VariableOrNumber CreateStringVariable(Func evaluator) - { - return new VariableOrNumber( - "", - (RPMVesselComputer _) => evaluator(), - null, - VariableUpdateType.Volatile, - rpmComp - ); - } } } diff --git a/RasterPropMonitor/Core/RasterPropMonitorComputer.cs b/RasterPropMonitor/Core/RasterPropMonitorComputer.cs index 2967f990..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*"); @@ -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/Core/StringProcessor.cs b/RasterPropMonitor/Core/StringProcessor.cs index 3498c7b5..f13929d9 100644 --- a/RasterPropMonitor/Core/StringProcessor.cs +++ b/RasterPropMonitor/Core/StringProcessor.cs @@ -60,16 +60,7 @@ public StringProcessorFormatter(string input, RasterPropMonitorComputer rpmComp, sourceVariables = new VariableOrNumber[sourceVarStrings.Length]; for (int i = 0; i < sourceVarStrings.Length; ++i ) { - var variableName = sourceVarStrings[i]; - if (variableName.StartsWith("MONITOR_LOCAL_")) - { - sourceVariables[i] = rpm.GetVariable(variableName); - } - else - { - sourceVariables[i] = rpmComp.InstantiateVariableOrNumber(variableName); - } - + sourceVariables[i] = rpmComp.InstantiateVariableOrNumber(sourceVarStrings[i]); allVariablesConstant = allVariablesConstant && sourceVariables[i].isConstant; } sourceValues = new object[sourceVariables.Length]; diff --git a/RasterPropMonitor/Handlers/JSIOrbitDisplay.cs b/RasterPropMonitor/Handlers/JSIOrbitDisplay.cs index 0c0ac836..ef6508f7 100644 --- a/RasterPropMonitor/Handlers/JSIOrbitDisplay.cs +++ b/RasterPropMonitor/Handlers/JSIOrbitDisplay.cs @@ -27,7 +27,7 @@ namespace JSI { - public class JSIOrbitDisplay : InternalModule, IPageElement + public class JSIOrbitDisplay : InternalModule { [KSPField] public string backgroundColor = string.Empty; @@ -65,7 +65,6 @@ public class JSIOrbitDisplay : InternalModule, IPageElement private bool startupComplete; private Material lineMaterial; - private RasterPropMonitor rpm; static readonly int CIRCLE_POINTS = 60; static readonly int ORBIT_POINTS = 60; @@ -80,11 +79,6 @@ public override void OnAwake() } } - void IPageElement.HandlePageCreate(RasterPropMonitor rpm) - { - this.rpm = rpm; - } - // TODO: this could all be improved by implementint adaptive screen-space tesselation: // http://blog.johannesmp.com/2022/06/30/KSP2-Dev-Diary_Orbit-Tessellation/ @@ -327,7 +321,8 @@ public bool RenderOrbit(RenderTexture screen, float cameraAspect) double horizPixelSize = displayPosition.z - iconPixelSize; double vertPixelSize = displayPosition.w - iconPixelSize; - (int patchIndex, Orbit selectedPatch) = rpm.GetSelectedPatch(); + 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 From 382dea93eb72b9c06dc66372161e14266fcc9b97 Mon Sep 17 00:00:00 2001 From: JonnyOThan Date: Fri, 3 Oct 2025 10:02:36 -0400 Subject: [PATCH 11/12] remove some vestiges of monitor-local variables --- RasterPropMonitor/Core/StringProcessor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RasterPropMonitor/Core/StringProcessor.cs b/RasterPropMonitor/Core/StringProcessor.cs index 95dee35b..c6f13e9f 100644 --- a/RasterPropMonitor/Core/StringProcessor.cs +++ b/RasterPropMonitor/Core/StringProcessor.cs @@ -39,7 +39,7 @@ public class StringProcessorFormatter public string cachedResult; // TODO: Add support for multi-line processed support. - public StringProcessorFormatter(string input, RasterPropMonitorComputer rpmComp, RasterPropMonitor rpm = null) + public StringProcessorFormatter(string input, RasterPropMonitorComputer rpmComp) { if(string.IsNullOrEmpty(input)) { From 2ebebf154c2f1f2c56d6b8924f043bfb86089126 Mon Sep 17 00:00:00 2001 From: JonnyOThan Date: Fri, 3 Oct 2025 10:03:17 -0400 Subject: [PATCH 12/12] remove some vestiges of monitor-local variables --- RasterPropMonitor/Core/MonitorPage.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RasterPropMonitor/Core/MonitorPage.cs b/RasterPropMonitor/Core/MonitorPage.cs index 1127e70f..d2ac410e 100644 --- a/RasterPropMonitor/Core/MonitorPage.cs +++ b/RasterPropMonitor/Core/MonitorPage.cs @@ -138,7 +138,7 @@ public void UpdateText(RasterPropMonitorComputer rpmComp) allTextConstant = true; for (int i = 0; i < linesArray.Length; ++i) { - spf[i] = new StringProcessorFormatter(linesArray[i], rpmComp, ourMonitor); + spf[i] = new StringProcessorFormatter(linesArray[i], rpmComp); outputLines[i] = spf[i].cachedResult;